First pass at typescript support
Oh man did this end up requiring a *ton* of other work as well. There's still a few typing issues I still can't quite work out, and others I'd like to improve when I have time. In fact, this version doesn't even really work, it has a stack overflow error caused by a tooltip for some reason have a tree inside it, which in turn has another tooltip, etc. There's also 17 errors that I *really* feel like shouldn't be there, but they are, and 113 warnings - mostly using ! to assert that things are non-null. Lots of work left to do, to sum up. The reason I'm committing this now is because I really need to get to work on my game jam, and since it won't use a tree or really many of TMT-X's features, I can get away with using a broken engine :)
This commit is contained in:
parent
c4a3a0de4d
commit
0afcd1cd3d
134 changed files with 16132 additions and 7106 deletions
20
.eslintrc.js
Normal file
20
.eslintrc.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
extends: [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended",
|
||||
"@vue/typescript/recommended",
|
||||
"@vue/prettier",
|
||||
"@vue/prettier/@typescript-eslint"
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020
|
||||
},
|
||||
rules: {
|
||||
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off"
|
||||
}
|
||||
};
|
4
.prettierrc.json
Normal file
4
.prettierrc.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"printWidth": 100,
|
||||
"tabWidth": 4
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
presets: ["@vue/cli-plugin-babel/preset"]
|
||||
};
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"include": [
|
||||
"./src/components/**/*"
|
||||
]
|
||||
}
|
2726
package-lock.json
generated
2726
package-lock.json
generated
File diff suppressed because it is too large
Load diff
45
package.json
45
package.json
|
@ -8,44 +8,49 @@
|
|||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ivanv/vue-collapse-transition": "^1.0.2",
|
||||
"core-js": "^3.6.5",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"vue": "^3.1.4",
|
||||
"vue-next-select": "^2.7.0",
|
||||
"vue": "^3.2.2",
|
||||
"vue-class-component": "^8.0.0-rc.1",
|
||||
"vue-next-select": "^2.9.0",
|
||||
"vue-sortable": "github:Netbel/vue-sortable#master-fix",
|
||||
"vue-textarea-autosize": "^1.1.1",
|
||||
"vue-transition-expand": "^0.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ivanv/vue-collapse-transition": "^1.0.2",
|
||||
"@types/lodash.clonedeep": "^4.5.6",
|
||||
"@typescript-eslint/eslint-plugin": "^4.18.0",
|
||||
"@typescript-eslint/parser": "^4.18.0",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-plugin-typescript": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"@vue/compiler-sfc": "^3.0.0-beta.1",
|
||||
"@vue/compiler-sfc": "^3.2.2",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"@vue/eslint-config-typescript": "^7.0.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^7.0.0-alpha.0",
|
||||
"prettier": "^1.19.1",
|
||||
"raw-loader": "^4.0.2",
|
||||
"sass": "^1.36.0",
|
||||
"sass-loader": "^10.2.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"rules": {}
|
||||
"sass-loader": "^10.2.0",
|
||||
"tsconfig-paths-webpack-plugin": "^3.5.1",
|
||||
"typescript": "~4.1.5"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
],
|
||||
"gitHooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,vue}": [
|
||||
"vue-cli-service lint",
|
||||
"git add"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
79
src/App.vue
79
src/App.vue
|
@ -1,53 +1,54 @@
|
|||
<template>
|
||||
<div id="modal-root" :style="theme" />
|
||||
<div class="app" @mousemove="updateMouse" :style="theme" :class="{ useHeader }">
|
||||
<Nav v-if="useHeader" />
|
||||
<Tabs />
|
||||
<TPS v-if="showTPS" />
|
||||
<GameOverScreen />
|
||||
<NaNScreen />
|
||||
</div>
|
||||
<div id="modal-root" :style="theme" />
|
||||
<div class="app" @mousemove="updateMouse" :style="theme" :class="{ useHeader }">
|
||||
<Nav v-if="useHeader" />
|
||||
<Tabs />
|
||||
<TPS v-if="showTPS" />
|
||||
<GameOverScreen />
|
||||
<NaNScreen />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import themes from './data/themes';
|
||||
import player from './game/player';
|
||||
import modInfo from './data/modInfo.json';
|
||||
import { mapState } from './util/vue';
|
||||
import './main.css';
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import themes from "./data/themes";
|
||||
import player from "./game/player";
|
||||
import modInfo from "./data/modInfo.json";
|
||||
import { mapState } from "./util/vue";
|
||||
import "./main.css";
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
data() {
|
||||
return { useHeader: modInfo.useHeader };
|
||||
},
|
||||
computed: {
|
||||
...mapState([ 'showTPS' ]),
|
||||
theme() {
|
||||
return themes[player.theme].variables;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateMouse(/* event */) {
|
||||
// TODO use event to update mouse position for particles
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "App",
|
||||
data() {
|
||||
return { useHeader: modInfo.useHeader };
|
||||
},
|
||||
computed: {
|
||||
...mapState(["showTPS"]),
|
||||
theme() {
|
||||
return themes[player.theme].variables;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateMouse(/* event */) {
|
||||
// TODO use event to update mouse position for particles
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app {
|
||||
background-color: var(--background);
|
||||
color: var(--color);
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--background);
|
||||
color: var(--color);
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#modal-root {
|
||||
position: absolute;
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,52 +1,66 @@
|
|||
<template>
|
||||
<tooltip v-if="achievement.unlocked" :display="tooltip">
|
||||
<div :style="style"
|
||||
:class="{ [layer || tab.layer]: true, feature: true, achievement: true, locked: !achievement.earned,
|
||||
bought: achievement.earned }">
|
||||
<component v-if="display" :is="display" />
|
||||
<branch-node :branches="achievement.branches" :id="id" featureType="achievement" />
|
||||
</div>
|
||||
</tooltip>
|
||||
<tooltip v-if="achievement.unlocked" :display="tooltip">
|
||||
<div
|
||||
:style="style"
|
||||
:class="{
|
||||
[layer]: true,
|
||||
feature: true,
|
||||
achievement: true,
|
||||
locked: !achievement.earned,
|
||||
bought: achievement.earned
|
||||
}"
|
||||
>
|
||||
<component v-if="display" :is="display" />
|
||||
<branch-node :branches="achievement.branches" :id="id" featureType="achievement" />
|
||||
</div>
|
||||
</tooltip>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { CoercableComponent } from "@/typings/component";
|
||||
import { Achievement } from "@/typings/features/achievement";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'achievement',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
id: [ Number, String ]
|
||||
},
|
||||
computed: {
|
||||
achievement() {
|
||||
return layers[this.layer || this.tab.layer].achievements[this.id];
|
||||
},
|
||||
style() {
|
||||
return [
|
||||
layers[this.layer || this.tab.layer].componentStyles?.achievement,
|
||||
this.achievement.style,
|
||||
this.achievement.image && this.achievement.earned ? {
|
||||
backgroundImage: `url(${this.achievement.image}`
|
||||
} : null
|
||||
];
|
||||
},
|
||||
display() {
|
||||
if (this.achievement.display) {
|
||||
return coerceComponent(this.achievement.display, 'h3');
|
||||
}
|
||||
return coerceComponent(this.achievement.name, 'h3');
|
||||
},
|
||||
tooltip() {
|
||||
if (this.achievement.earned) {
|
||||
return this.achievement.doneTooltip || this.achievement.tooltip || "You did it!";
|
||||
}
|
||||
return this.achievement.goalTooltip || this.achievement.tooltip || "LOCKED";
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "achievement",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
achievement(): Achievement {
|
||||
return layers[this.layer].achievements!.data[this.id];
|
||||
},
|
||||
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [
|
||||
layers[this.layer].componentStyles?.achievement,
|
||||
this.achievement.style,
|
||||
this.achievement.image && this.achievement.earned
|
||||
? {
|
||||
backgroundImage: `url(${this.achievement.image}`
|
||||
}
|
||||
: undefined
|
||||
];
|
||||
},
|
||||
display(): Component | string {
|
||||
if (this.achievement.display) {
|
||||
return coerceComponent(this.achievement.display, "h3");
|
||||
}
|
||||
return coerceComponent(this.achievement.name!, "h3");
|
||||
},
|
||||
tooltip(): CoercableComponent {
|
||||
if (this.achievement.earned) {
|
||||
return this.achievement.doneTooltip || this.achievement.tooltip || "You did it!";
|
||||
}
|
||||
return this.achievement.goalTooltip || this.achievement.tooltip || "LOCKED";
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -1,36 +1,48 @@
|
|||
<template>
|
||||
<div v-if="filteredAchievements" class="table">
|
||||
<template v-if="filteredAchievements.rows && filteredAchievements.cols">
|
||||
<div v-for="row in filteredAchievements.rows" class="row" :key="row">
|
||||
<div v-for="col in filteredAchievements.cols" :key="col">
|
||||
<achievement v-if="filteredAchievements[row * 10 + col] !== undefined" class="align" :id="row * 10 + col" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-frag v-else>
|
||||
<achievement v-for="(achievement, id) in filteredAchievements" :key="id" :id="id" />
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="filteredAchievements" class="table">
|
||||
<template v-if="filteredAchievements.rows && filteredAchievements.cols">
|
||||
<div v-for="row in filteredAchievements.rows" class="row" :key="row">
|
||||
<div v-for="col in filteredAchievements.cols" :key="col">
|
||||
<achievement
|
||||
v-if="filteredAchievements[row * 10 + col] !== undefined"
|
||||
class="align"
|
||||
:id="row * 10 + col"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-frag v-else>
|
||||
<achievement v-for="(achievement, id) in filteredAchievements" :key="id" :id="id" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { getFiltered } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { Achievement } from "@/typings/features/achievement";
|
||||
import { getFiltered, InjectLayerMixin } from "@/util/vue";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'achievements',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
achievements: Array
|
||||
},
|
||||
computed: {
|
||||
filteredAchievements() {
|
||||
return getFiltered(layers[this.layer || this.tab.layer].achievements, this.achievements);
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "achievements",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
achievements: {
|
||||
type: Object as PropType<Array<string>>
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredAchievements(): Record<string, Achievement> {
|
||||
if (layers[this.layer].achievements) {
|
||||
return getFiltered<Achievement>(
|
||||
layers[this.layer].achievements!.data,
|
||||
this.achievements
|
||||
);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -1,100 +1,104 @@
|
|||
<template>
|
||||
<div v-if="bar.unlocked" :style="style" class="bar">
|
||||
<div class="overlayTextContainer border" :style="borderStyle">
|
||||
<component class="overlayText" :style="textStyle" :is="display" />
|
||||
</div>
|
||||
<div class="border" :style="backgroundStyle">
|
||||
<div class="fill" :style="fillStyle" />
|
||||
</div>
|
||||
<branch-node :branches="bar.branches" :id="id" featureType="bar" />
|
||||
</div>
|
||||
<div v-if="bar.unlocked" :style="style" :class="{ [layer]: true, bar: true }">
|
||||
<div class="overlayTextContainer border" :style="borderStyle">
|
||||
<component class="overlayText" :style="textStyle" :is="display" />
|
||||
</div>
|
||||
<div class="border" :style="backgroundStyle">
|
||||
<div class="fill" :style="fillStyle" />
|
||||
</div>
|
||||
<branch-node :branches="bar.branches" :id="id" featureType="bar" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { UP, DOWN, LEFT, RIGHT, DEFAULT, coerceComponent } from '../../util/vue';
|
||||
import Decimal from '../../util/bignum';
|
||||
<script lang="ts">
|
||||
import { Direction } from "@/game/enums";
|
||||
import { layers } from "@/game/layers";
|
||||
import { Bar } from "@/typings/features/bar";
|
||||
import Decimal from "@/util/bignum";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'bar',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
id: [ Number, String ]
|
||||
},
|
||||
computed: {
|
||||
bar() {
|
||||
return layers[this.layer || this.tab.layer].bars[this.id];
|
||||
},
|
||||
progress() {
|
||||
let progress = this.bar.progress instanceof Decimal ? this.bar.progress.toNumber() : this.bar.progress;
|
||||
return (1 - Math.min(Math.max(progress, 0), 1)) * 100;
|
||||
},
|
||||
style() {
|
||||
return [
|
||||
{ 'width': this.bar.width + "px", 'height': this.bar.height + "px" },
|
||||
layers[this.layer || this.tab.layer].componentStyles?.bar,
|
||||
this.bar.style
|
||||
];
|
||||
},
|
||||
borderStyle() {
|
||||
return [
|
||||
{ 'width': this.bar.width + "px", 'height': this.bar.height + "px" },
|
||||
this.bar.borderStyle
|
||||
];
|
||||
},
|
||||
textStyle() {
|
||||
return [
|
||||
this.bar.style,
|
||||
this.bar.textStyle
|
||||
];
|
||||
},
|
||||
backgroundStyle() {
|
||||
return [
|
||||
{ 'width': this.bar.width + "px", 'height': this.bar.height + "px" },
|
||||
this.bar.style,
|
||||
this.bar.baseStyle,
|
||||
this.bar.borderStyle
|
||||
];
|
||||
},
|
||||
fillStyle() {
|
||||
const fillStyle = { 'width': (this.bar.width + 0.5) + "px", 'height': (this.bar.height + 0.5) + "px" };
|
||||
switch (this.bar.direction) {
|
||||
case UP:
|
||||
fillStyle['clip-path'] = `inset(${this.progress}% 0% 0% 0%)`;
|
||||
fillStyle.width = this.bar.width + 1 + 'px';
|
||||
break;
|
||||
case DOWN:
|
||||
fillStyle['clip-path'] = `inset(0% 0% ${this.progress}% 0%)`;
|
||||
fillStyle.width = this.bar.width + 1 + 'px';
|
||||
break;
|
||||
case RIGHT:
|
||||
fillStyle['clip-path'] = `inset(0% ${this.progress}% 0% 0%)`;
|
||||
break;
|
||||
case LEFT:
|
||||
fillStyle['clip-path'] = `inset(0% 0% 0% ${this.progress} + '%)`;
|
||||
break;
|
||||
case DEFAULT:
|
||||
fillStyle['clip-path'] = 'inset(0% 50% 0% 0%)';
|
||||
break;
|
||||
}
|
||||
return [
|
||||
fillStyle,
|
||||
this.bar.style,
|
||||
this.bar.fillStyle
|
||||
];
|
||||
},
|
||||
display() {
|
||||
return coerceComponent(this.bar.display);
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "bar",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
bar(): Bar {
|
||||
return layers[this.layer].bars!.data[this.id];
|
||||
},
|
||||
progress(): number {
|
||||
let progress =
|
||||
this.bar.progress instanceof Decimal
|
||||
? this.bar.progress.toNumber()
|
||||
: (this.bar.progress as number);
|
||||
return (1 - Math.min(Math.max(progress, 0), 1)) * 100;
|
||||
},
|
||||
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [
|
||||
{ width: this.bar.width + "px", height: this.bar.height + "px" },
|
||||
layers[this.layer].componentStyles?.bar,
|
||||
this.bar.style
|
||||
];
|
||||
},
|
||||
borderStyle(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [
|
||||
{ width: this.bar.width + "px", height: this.bar.height + "px" },
|
||||
this.bar.borderStyle
|
||||
];
|
||||
},
|
||||
textStyle(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [this.bar.style, this.bar.textStyle];
|
||||
},
|
||||
backgroundStyle(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [
|
||||
{ width: this.bar.width + "px", height: this.bar.height + "px" },
|
||||
this.bar.style,
|
||||
this.bar.baseStyle,
|
||||
this.bar.borderStyle
|
||||
];
|
||||
},
|
||||
fillStyle(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
const fillStyle: Partial<CSSStyleDeclaration> = {
|
||||
width: this.bar.width + 0.5 + "px",
|
||||
height: this.bar.height + 0.5 + "px"
|
||||
};
|
||||
switch (this.bar.direction) {
|
||||
case Direction.Up:
|
||||
fillStyle.clipPath = `inset(${this.progress}% 0% 0% 0%)`;
|
||||
fillStyle.width = this.bar.width + 1 + "px";
|
||||
break;
|
||||
case Direction.Down:
|
||||
fillStyle.clipPath = `inset(0% 0% ${this.progress}% 0%)`;
|
||||
fillStyle.width = this.bar.width + 1 + "px";
|
||||
break;
|
||||
case Direction.Right:
|
||||
fillStyle.clipPath = `inset(0% ${this.progress}% 0% 0%)`;
|
||||
break;
|
||||
case Direction.Left:
|
||||
fillStyle.clipPath = `inset(0% 0% 0% ${this.progress} + '%)`;
|
||||
break;
|
||||
case Direction.Default:
|
||||
fillStyle.clipPath = "inset(0% 50% 0% 0%)";
|
||||
break;
|
||||
}
|
||||
return [fillStyle, this.bar.style, this.bar.fillStyle];
|
||||
},
|
||||
display(): Component | string {
|
||||
return coerceComponent(this.bar.display);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bar {
|
||||
position: relative;
|
||||
display: table;
|
||||
position: relative;
|
||||
display: table;
|
||||
}
|
||||
|
||||
.overlayTextContainer {
|
||||
|
@ -103,19 +107,19 @@ export default {
|
|||
vertical-align: middle;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
z-index: 3;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.overlayText {
|
||||
z-index: 6;
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
.border {
|
||||
border: 2px solid;
|
||||
border: 2px solid;
|
||||
border-radius: 10px;
|
||||
border-color: var(--color);
|
||||
overflow: hidden;
|
||||
-webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC);
|
||||
mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,91 +1,137 @@
|
|||
<template>
|
||||
<div v-if="buyable.unlocked" style="display: grid">
|
||||
<button :style="style" @click="buyable.buy" @mousedown="start" @mouseleave="stop" @mouseup="stop" @touchstart="start"
|
||||
:class="{ feature: true, [layer || tab.layer]: true, buyable: true, can: buyable.canBuy, locked: !buyable.canAfford, bought }"
|
||||
@touchend="stop" @touchcancel="stop" :disabled="!buyable.canBuy">
|
||||
<div v-if="title">
|
||||
<component :is="title" />
|
||||
</div>
|
||||
<component :is="display" style="white-space: pre-line;" />
|
||||
<mark-node :mark="buyable.mark" />
|
||||
<branch-node :branches="buyable.branches" :id="id" featureType="buyable" />
|
||||
</button>
|
||||
<div v-if="(buyable.sellOne !== undefined && buyable.canSellOne !== false) ||
|
||||
(buyable.sellAll !== undefined && buyable.canSellAll !== false)" style="width: 100%">
|
||||
<button @click="buyable.sellAll" v-if="buyable.sellAll !== undefined && buyable.canSellAll !== false"
|
||||
:class="{ 'buyable-button': true, can: buyable.unlocked, locked: !buyable.unlocked, feature: true }"
|
||||
:style="{ 'background-color': buyable.canSellAll ? layerColor : '' }">
|
||||
Sell All
|
||||
</button>
|
||||
<button @click="buyable.sellOne" v-if="buyable.sellOne !== undefined && buyable.canSellOne !== false"
|
||||
:class="{ 'buyable-button': true, can: buyable.unlocked, locked: !buyable.unlocked, feature: true }"
|
||||
:style="{ 'background-color': buyable.canSellOne ? layerColor : '' }">
|
||||
Sell One
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="buyable.unlocked" style="display: grid">
|
||||
<button
|
||||
:style="style"
|
||||
@click="buyable.buy"
|
||||
@mousedown="start"
|
||||
@mouseleave="stop"
|
||||
@mouseup="stop"
|
||||
@touchstart="start"
|
||||
:class="{
|
||||
feature: true,
|
||||
[layer]: true,
|
||||
buyable: true,
|
||||
can: buyable.canBuy,
|
||||
locked: !buyable.canAfford,
|
||||
bought
|
||||
}"
|
||||
@touchend="stop"
|
||||
@touchcancel="stop"
|
||||
:disabled="!buyable.canBuy"
|
||||
>
|
||||
<div v-if="title">
|
||||
<component :is="title" />
|
||||
</div>
|
||||
<component :is="display" style="white-space: pre-line;" />
|
||||
<mark-node :mark="buyable.mark" />
|
||||
<branch-node :branches="buyable.branches" :id="id" featureType="buyable" />
|
||||
</button>
|
||||
<div
|
||||
v-if="
|
||||
(buyable.sellOne !== undefined && buyable.canSellOne !== false) ||
|
||||
(buyable.sellAll !== undefined && buyable.canSellAll !== false)
|
||||
"
|
||||
style="width: 100%"
|
||||
>
|
||||
<button
|
||||
@click="buyable.sellAll"
|
||||
v-if="buyable.sellAll !== undefined && buyable.canSellAll !== false"
|
||||
:class="{
|
||||
'buyable-button': true,
|
||||
can: buyable.unlocked,
|
||||
locked: !buyable.unlocked,
|
||||
feature: true
|
||||
}"
|
||||
:style="{ 'background-color': buyable.canSellAll ? layerColor : '' }"
|
||||
>
|
||||
Sell All
|
||||
</button>
|
||||
<button
|
||||
@click="buyable.sellOne"
|
||||
v-if="buyable.sellOne !== undefined && buyable.canSellOne !== false"
|
||||
:class="{
|
||||
'buyable-button': true,
|
||||
can: buyable.unlocked,
|
||||
locked: !buyable.unlocked,
|
||||
feature: true
|
||||
}"
|
||||
:style="{ 'background-color': buyable.canSellOne ? layerColor : '' }"
|
||||
>
|
||||
Sell One
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import player from '../../game/player';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { Buyable } from "@/typings/features/buyable";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'buyable',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
id: [ Number, String ],
|
||||
size: [ Number, String ]
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
interval: false,
|
||||
time: 0
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
buyable() {
|
||||
return layers[this.layer || this.tab.layer].buyables[this.id];
|
||||
},
|
||||
bought() {
|
||||
return player[this.layer || this.tab.layer].buyables[this.id].gte(this.buyable.purchaseLimit);
|
||||
},
|
||||
style() {
|
||||
return [
|
||||
this.buyable.canBuy ? { 'background-color': layers[this.layer || this.tab.layer].color } : {},
|
||||
this.size ? { 'height': this.size, 'width': this.size } : {},
|
||||
layers[this.layer || this.tab.layer].componentStyles?.buyable,
|
||||
this.buyable.style
|
||||
];
|
||||
},
|
||||
title() {
|
||||
if (this.buyable.title) {
|
||||
return coerceComponent(this.buyable.title, 'h2');
|
||||
}
|
||||
return null;
|
||||
},
|
||||
display() {
|
||||
return coerceComponent(this.buyable.display, 'div');
|
||||
},
|
||||
layerColor() {
|
||||
return layers[this.layer || this.tab.layer].color;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
start() {
|
||||
if (!this.interval) {
|
||||
this.interval = setInterval(this.buyable.buy, 250);
|
||||
}
|
||||
},
|
||||
stop() {
|
||||
clearInterval(this.interval);
|
||||
this.interval = false;
|
||||
this.time = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "buyable",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
},
|
||||
size: Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
interval: null,
|
||||
time: 0
|
||||
} as {
|
||||
interval: number | null;
|
||||
time: number;
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
buyable(): Buyable {
|
||||
return layers[this.layer].buyables!.data[this.id];
|
||||
},
|
||||
bought(): boolean {
|
||||
return player.layers[this.layer].buyables[this.id].gte(this.buyable.purchaseLimit);
|
||||
},
|
||||
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [
|
||||
this.buyable.canBuy ? { backgroundColor: layers[this.layer].color } : undefined,
|
||||
this.size ? { height: this.size + "px", width: this.size + "px" } : undefined,
|
||||
layers[this.layer].componentStyles?.buyable,
|
||||
this.buyable.style
|
||||
];
|
||||
},
|
||||
title(): Component | string | null {
|
||||
if (this.buyable.title) {
|
||||
return coerceComponent(this.buyable.title, "h2");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
display(): Component | string {
|
||||
return coerceComponent(this.buyable.display, "div");
|
||||
},
|
||||
layerColor(): string {
|
||||
return layers[this.layer].color;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
start() {
|
||||
if (!this.interval) {
|
||||
this.interval = setInterval(this.buyable.buy, 250);
|
||||
}
|
||||
},
|
||||
stop() {
|
||||
if (this.interval) {
|
||||
clearInterval(this.interval);
|
||||
this.interval = null;
|
||||
this.time = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -96,6 +142,6 @@ export default {
|
|||
}
|
||||
|
||||
.buyable-button {
|
||||
width: calc(100% - 10px);
|
||||
width: calc(100% - 10px);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,68 +1,90 @@
|
|||
<template>
|
||||
<div v-if="filteredBuyables" class="table">
|
||||
<respec-button v-if="showRespec" style="margin-bottom: 12px;" :confirmRespec="confirmRespec"
|
||||
@set-confirm-respec="setConfirmRespec" @respec="respec" />
|
||||
<template v-if="filteredBuyables.rows && filteredBuyables.cols">
|
||||
<div v-for="row in filteredBuyables.rows" class="row" :key="row">
|
||||
<div v-for="col in filteredBuyables.cols" :key="col">
|
||||
<buyable v-if="filteredBuyables[row * 10 + col] !== undefined" class="align buyable-container" :style="{ height }"
|
||||
:id="row * 10 + col" :size="height === 'inherit' ? null : height" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<row v-else>
|
||||
<buyable v-for="(buyable, id) in filteredBuyables" :key="id" class="align buyable-container"
|
||||
:style="{ height }" :id="id" :size="height === 'inherit' ? null : height" />
|
||||
</row>
|
||||
</div>
|
||||
<div v-if="filteredBuyables" class="table">
|
||||
<respec-button
|
||||
v-if="showRespec"
|
||||
style="margin-bottom: 12px;"
|
||||
:confirmRespec="confirmRespec"
|
||||
:respecWarningDisplay="respecWarningDisplay"
|
||||
@set-confirm-respec="setConfirmRespec"
|
||||
@respec="respec"
|
||||
/>
|
||||
<template v-if="filteredBuyables.rows && filteredBuyables.cols">
|
||||
<div v-for="row in filteredBuyables.rows" class="row" :key="row">
|
||||
<div v-for="col in filteredBuyables.cols" :key="col">
|
||||
<buyable
|
||||
v-if="filteredBuyables[row * 10 + col] !== undefined"
|
||||
class="align buyable-container"
|
||||
:style="{ height }"
|
||||
:id="row * 10 + col"
|
||||
:size="height === 'inherit' ? null : height"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<row v-else>
|
||||
<buyable
|
||||
v-for="(buyable, id) in filteredBuyables"
|
||||
:key="id"
|
||||
class="align buyable-container"
|
||||
:style="{ height }"
|
||||
:id="id"
|
||||
:size="height === 'inherit' ? null : height"
|
||||
/>
|
||||
</row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import player from '../../game/player';
|
||||
import { getFiltered } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { CoercableComponent } from "@/typings/component";
|
||||
import { Buyable } from "@/typings/features/buyable";
|
||||
import { getFiltered, InjectLayerMixin } from "@/util/vue";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'buyables',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
buyables: Array,
|
||||
height: {
|
||||
type: [ Number, String ],
|
||||
default: "inherit"
|
||||
}
|
||||
},
|
||||
emits: [ 'set-confirm-respec' ],
|
||||
computed: {
|
||||
filteredBuyables() {
|
||||
return getFiltered(layers[this.layer || this.tab.layer].buyables, this.buyables);
|
||||
},
|
||||
showRespec() {
|
||||
return layers[this.layer || this.tab.layer].buyables.showRespec;
|
||||
},
|
||||
confirmRespec() {
|
||||
return player[this.layer || this.tab.layer].buyables.confirmRespec;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setConfirmRespec(value) {
|
||||
if (this.confirmRespec != undefined) {
|
||||
this.$emit("set-confirm-respec", value);
|
||||
} else {
|
||||
player[this.layer || this.tab.layer].buyables.confirmRespec = value;
|
||||
}
|
||||
},
|
||||
respec() {
|
||||
layers[this.layer || this.tab.layer].buyables.respec?.();
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "buyables",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
buyables: {
|
||||
type: Object as PropType<Array<string>>
|
||||
},
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: "inherit"
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredBuyables(): Record<string, Buyable> {
|
||||
if (layers[this.layer].buyables) {
|
||||
return getFiltered<Buyable>(layers[this.layer].buyables!.data, this.buyables);
|
||||
}
|
||||
return {};
|
||||
},
|
||||
showRespec(): boolean | undefined {
|
||||
return layers[this.layer].buyables!.showRespecButton;
|
||||
},
|
||||
confirmRespec(): boolean {
|
||||
return player.layers[this.layer].confirmRespecBuyables;
|
||||
},
|
||||
respecWarningDisplay(): CoercableComponent | undefined {
|
||||
return layers[this.layer].buyables?.respecWarningDisplay;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setConfirmRespec(value: boolean) {
|
||||
player.layers[this.layer].confirmRespecBuyables = value;
|
||||
},
|
||||
respec() {
|
||||
layers[this.layer].buyables!.respec?.();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.buyable-container {
|
||||
margin-left: 7px;
|
||||
margin-right: 7px;
|
||||
margin-left: 7px;
|
||||
margin-right: 7px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,79 +1,84 @@
|
|||
<template>
|
||||
<div v-if="challenge.shown" :style="style"
|
||||
:class="{
|
||||
feature: true,
|
||||
challenge: true,
|
||||
resetNotify: challenge.active,
|
||||
notify: challenge.active && challenge.canComplete,
|
||||
done: challenge.completed,
|
||||
maxed: challenge.maxed
|
||||
}">
|
||||
<div v-if="title"><component :is="title" /></div>
|
||||
<button :style="{ backgroundColor: challenge.maxed ? null : buttonColor }" @click="toggle">
|
||||
{{ buttonText }}
|
||||
</button>
|
||||
<component v-if="fullDisplay" :is="fullDisplay" />
|
||||
<default-challenge-display v-else :id="id" />
|
||||
<mark-node :mark="challenge.mark" />
|
||||
<branch-node :branches="challenge.branches" :id="id" featureType="challenge" />
|
||||
</div>
|
||||
<div
|
||||
v-if="challenge.shown"
|
||||
:style="style"
|
||||
:class="{
|
||||
feature: true,
|
||||
[layer]: true,
|
||||
challenge: true,
|
||||
resetNotify: challenge.active,
|
||||
notify: challenge.active && challenge.canComplete,
|
||||
done: challenge.completed,
|
||||
maxed: challenge.maxed
|
||||
}"
|
||||
>
|
||||
<div v-if="title"><component :is="title" /></div>
|
||||
<button :style="{ backgroundColor: challenge.maxed ? null : buttonColor }" @click="toggle">
|
||||
{{ buttonText }}
|
||||
</button>
|
||||
<component v-if="fullDisplay" :is="fullDisplay" />
|
||||
<default-challenge-display v-else :id="id" />
|
||||
<mark-node :mark="challenge.mark" />
|
||||
<branch-node :branches="challenge.branches" :id="id" featureType="challenge" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { Challenge } from "@/typings/features/challenge";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'challenge',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
id: [ Number, String ]
|
||||
},
|
||||
computed: {
|
||||
challenge() {
|
||||
return layers[this.layer || this.tab.layer].challenges[this.id];
|
||||
},
|
||||
style() {
|
||||
return [
|
||||
layers[this.layer || this.tab.layer].componentStyles?.challenge,
|
||||
this.challenge.style
|
||||
];
|
||||
},
|
||||
title() {
|
||||
if (this.challenge.titleDisplay) {
|
||||
return coerceComponent(this.challenge.titleDisplay, 'div');
|
||||
}
|
||||
if (this.challenge.name) {
|
||||
return coerceComponent(this.challenge.name, 'h3');
|
||||
}
|
||||
return null;
|
||||
},
|
||||
buttonColor() {
|
||||
return layers[this.layer || this.tab.layer].color;
|
||||
},
|
||||
buttonText() {
|
||||
if (this.challenge.active) {
|
||||
return this.challenge.canComplete ? "Finish" : "Exit Early";
|
||||
}
|
||||
if (this.challenge.maxed) {
|
||||
return "Completed";
|
||||
}
|
||||
return "Start";
|
||||
},
|
||||
fullDisplay() {
|
||||
if (this.challenge.fullDisplay) {
|
||||
return coerceComponent(this.challenge.fullDisplay, 'div');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
this.challenge.toggle();
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "challenge",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
challenge(): Challenge {
|
||||
return layers[this.layer].challenges!.data[this.id];
|
||||
},
|
||||
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [layers[this.layer].componentStyles?.challenge, this.challenge.style];
|
||||
},
|
||||
title(): Component | string | null {
|
||||
if (this.challenge.titleDisplay) {
|
||||
return coerceComponent(this.challenge.titleDisplay, "div");
|
||||
}
|
||||
if (this.challenge.name) {
|
||||
return coerceComponent(this.challenge.name, "h3");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
buttonColor(): string {
|
||||
return layers[this.layer].color;
|
||||
},
|
||||
buttonText(): string {
|
||||
if (this.challenge.active) {
|
||||
return this.challenge.canComplete ? "Finish" : "Exit Early";
|
||||
}
|
||||
if (this.challenge.maxed) {
|
||||
return "Completed";
|
||||
}
|
||||
return "Start";
|
||||
},
|
||||
fullDisplay(): Component | string | null {
|
||||
if (this.challenge.fullDisplay) {
|
||||
return coerceComponent(this.challenge.fullDisplay, "div");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
this.challenge.toggle();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -102,6 +107,6 @@ export default {
|
|||
}
|
||||
|
||||
.challenge.maxed button {
|
||||
cursor: unset;
|
||||
cursor: unset;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,36 +1,41 @@
|
|||
<template>
|
||||
<div v-if="filteredChallenges" class="table">
|
||||
<template v-if="filteredChallenges.rows && filteredChallenges.cols">
|
||||
<div v-for="row in filteredChallenges.rows" class="row" :key="row">
|
||||
<div v-for="col in filteredChallenges.cols" :key="col">
|
||||
<challenge v-if="filteredChallenges[row * 10 + col] !== undefined" :id="row * 10 + col" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<row v-else>
|
||||
<challenge v-for="(challenge, id) in filteredChallenges" :key="id" :id="id" />
|
||||
</row>
|
||||
</div>
|
||||
<div v-if="filteredChallenges" class="table">
|
||||
<template v-if="filteredChallenges.rows && filteredChallenges.cols">
|
||||
<div v-for="row in filteredChallenges.rows" class="row" :key="row">
|
||||
<div v-for="col in filteredChallenges.cols" :key="col">
|
||||
<challenge
|
||||
v-if="filteredChallenges[row * 10 + col] !== undefined"
|
||||
:id="row * 10 + col"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<row v-else>
|
||||
<challenge v-for="(challenge, id) in filteredChallenges" :key="id" :id="id" />
|
||||
</row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { getFiltered } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { Challenge } from "@/typings/features/challenge";
|
||||
import { getFiltered, InjectLayerMixin } from "@/util/vue";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'challenges',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
challenges: Array
|
||||
},
|
||||
computed: {
|
||||
filteredChallenges() {
|
||||
return getFiltered(layers[this.layer || this.tab.layer].challenges, this.challenges);
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "challenges",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
challenges: {
|
||||
type: Object as PropType<Array<string>>
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredChallenges(): Record<string, Challenge> {
|
||||
return getFiltered(layers[this.layer].challenges!.data, this.challenges);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -1,73 +1,95 @@
|
|||
<template>
|
||||
<div v-if="clickable.unlocked">
|
||||
<button :style="style" @click="clickable.click" @mousedown="start" @mouseleave="stop" @mouseup="stop" @touchstart="start"
|
||||
@touchend="stop" @touchcancel="stop" :disabled="!clickable.canClick"
|
||||
:class="{ feature: true, [layer || tab.layer]: true, clickable: true, can: clickable.canClick, locked: !clickable.canClick }">
|
||||
<div v-if="title">
|
||||
<component :is="title" />
|
||||
</div>
|
||||
<component :is="display" style="white-space: pre-line;" />
|
||||
<mark-node :mark="clickable.mark" />
|
||||
<branch-node :branches="clickable.branches" :id="id" featureType="clickable" />
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="clickable.unlocked">
|
||||
<button
|
||||
:style="style"
|
||||
@click="clickable.click"
|
||||
@mousedown="start"
|
||||
@mouseleave="stop"
|
||||
@mouseup="stop"
|
||||
@touchstart="start"
|
||||
@touchend="stop"
|
||||
@touchcancel="stop"
|
||||
:disabled="!clickable.canClick"
|
||||
:class="{
|
||||
feature: true,
|
||||
[layer]: true,
|
||||
clickable: true,
|
||||
can: clickable.canClick,
|
||||
locked: !clickable.canClick
|
||||
}"
|
||||
>
|
||||
<div v-if="title">
|
||||
<component :is="title" />
|
||||
</div>
|
||||
<component :is="display" style="white-space: pre-line;" />
|
||||
<mark-node :mark="clickable.mark" />
|
||||
<branch-node :branches="clickable.branches" :id="id" featureType="clickable" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { Clickable } from "@/typings/features/clickable";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'clickable',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
id: [ Number, String ],
|
||||
size: {
|
||||
type: [ Number, String ]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
interval: false,
|
||||
time: 0
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
clickable() {
|
||||
return layers[this.layer || this.tab.layer].clickables[this.id];
|
||||
},
|
||||
style() {
|
||||
return [
|
||||
this.clickable.canClick ? { 'background-color': layers[this.layer || this.tab.layer].color } : {},
|
||||
this.size ? {'height': this.size, 'width': this.size} : {},
|
||||
layers[this.layer || this.tab.layer].componentStyles?.clickable,
|
||||
this.clickable.style
|
||||
];
|
||||
},
|
||||
title() {
|
||||
if (this.clickable.title) {
|
||||
return coerceComponent(this.clickable.title, 'h2');
|
||||
}
|
||||
return null;
|
||||
},
|
||||
display() {
|
||||
return coerceComponent(this.clickable.display, 'div');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
start() {
|
||||
if (!this.interval) {
|
||||
this.interval = setInterval(this.clickable.click, 250);
|
||||
}
|
||||
},
|
||||
stop() {
|
||||
clearInterval(this.interval);
|
||||
this.interval = false;
|
||||
this.time = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "clickable",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
},
|
||||
size: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
interval: null,
|
||||
time: 0
|
||||
} as {
|
||||
interval: number | null;
|
||||
time: number;
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
clickable(): Clickable {
|
||||
return layers[this.layer].clickables!.data[this.id];
|
||||
},
|
||||
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [
|
||||
this.clickable.canClick ? { backgroundColor: layers[this.layer].color } : undefined,
|
||||
this.size ? { height: this.size, width: this.size } : undefined,
|
||||
layers[this.layer].componentStyles?.clickable,
|
||||
this.clickable.style
|
||||
];
|
||||
},
|
||||
title(): Component | string | null {
|
||||
if (this.clickable.title) {
|
||||
return coerceComponent(this.clickable.title, "h2");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
display(): Component | string {
|
||||
return coerceComponent(this.clickable.display, "div");
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
start() {
|
||||
if (!this.interval && this.clickable.click) {
|
||||
this.interval = setInterval(this.clickable.click, 250);
|
||||
}
|
||||
},
|
||||
stop() {
|
||||
if (this.interval) {
|
||||
clearInterval(this.interval);
|
||||
this.interval = null;
|
||||
this.time = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -1,65 +1,79 @@
|
|||
<template>
|
||||
<div v-if="filteredClickables" class="table">
|
||||
<master-button v-if="showMasterButton" style="margin-bottom: 12px;" @press="press" />
|
||||
<template v-if="filteredClickables.rows && filteredClickables.cols">
|
||||
<div v-for="row in filteredClickables.rows" class="row" :key="row">
|
||||
<div v-for="col in filteredClickables.cols" :key="col">
|
||||
<clickable v-if="filteredClickables[row * 10 + col] !== undefined" class="align clickable-container"
|
||||
:style="{ height }" :id="row * 10 + col" :size="height === 'inherit' ? null : height" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<row v-else>
|
||||
<clickable v-for="(clickable, id) in filteredClickables" :key="id" class="align clickable-container" :style="{ height }"
|
||||
:id="id" :size="height === 'inherit' ? null : height" />
|
||||
</row>
|
||||
</div>
|
||||
<div v-if="filteredClickables" class="table">
|
||||
<master-button v-if="showMaster" style="margin-bottom: 12px;" @press="press" />
|
||||
<template v-if="filteredClickables.rows && filteredClickables.cols">
|
||||
<div v-for="row in filteredClickables.rows" class="row" :key="row">
|
||||
<div v-for="col in filteredClickables.cols" :key="col">
|
||||
<clickable
|
||||
v-if="filteredClickables[row * 10 + col] !== undefined"
|
||||
class="align clickable-container"
|
||||
:style="{ height }"
|
||||
:id="row * 10 + col"
|
||||
:size="height === 'inherit' ? null : height"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<row v-else>
|
||||
<clickable
|
||||
v-for="(clickable, id) in filteredClickables"
|
||||
:key="id"
|
||||
class="align clickable-container"
|
||||
:style="{ height }"
|
||||
:id="id"
|
||||
:size="height === 'inherit' ? null : height"
|
||||
/>
|
||||
</row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { getFiltered } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { Clickable } from "@/typings/features/clickable";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
import { layers } from "@/game/layers";
|
||||
import { getFiltered, InjectLayerMixin } from "@/util/vue";
|
||||
|
||||
export default {
|
||||
name: 'clickables',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
clickables: Array,
|
||||
showMaster: {
|
||||
type: Boolean,
|
||||
default: null
|
||||
},
|
||||
height: {
|
||||
type: [ Number, String ],
|
||||
default: "inherit"
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredClickables() {
|
||||
return getFiltered(layers[this.layer || this.tab.layer].clickables, this.clickables);
|
||||
},
|
||||
showMasterButton() {
|
||||
if (layers[this.layer || this.tab.layer].clickables?.masterButtonClick == undefined) {
|
||||
return false;
|
||||
}
|
||||
if (this.showMaster != undefined) {
|
||||
return this.showMaster;
|
||||
}
|
||||
return layers[this.layer || this.tab.layer].clickables?.showMaster;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
press() {
|
||||
layers[this.layer || this.tab.layer].clickables.masterButtonClick();
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "clickables",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
achievements: {
|
||||
type: Object as PropType<Array<string>>
|
||||
},
|
||||
showMasterButton: {
|
||||
type: Boolean,
|
||||
default: null
|
||||
},
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: "inherit"
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredClickables(): Record<string, Clickable> {
|
||||
return getFiltered(layers[this.layer].clickables!.data, this.clickables);
|
||||
},
|
||||
showMaster(): boolean | undefined {
|
||||
if (layers[this.layer].clickables?.masterButtonClick == undefined) {
|
||||
return false;
|
||||
}
|
||||
if (this.showMasterButton != undefined) {
|
||||
return this.showMasterButton;
|
||||
}
|
||||
return layers[this.layer].clickables?.showMasterButton;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
press() {
|
||||
layers[this.layer].clickables?.masterButtonClick?.();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.clickable-container {
|
||||
margin-left: 7px;
|
||||
margin-right: 7px;
|
||||
margin-left: 7px;
|
||||
margin-right: 7px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,46 +1,52 @@
|
|||
<template>
|
||||
<component :is="challengeDescription" v-bind="$attrs" />
|
||||
<div>Goal: <component :is="goalDescription" /></div>
|
||||
<div>Reward: <component :is="rewardDescription" /></div>
|
||||
<component v-if="rewardDisplay" :is="rewardDisplay" />
|
||||
<component :is="challengeDescription" v-bind="$attrs" />
|
||||
<div>Goal: <component :is="goalDescription" /></div>
|
||||
<div>Reward: <component :is="rewardDescription" /></div>
|
||||
<component v-if="rewardDisplay" :is="rewardDisplay" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { Challenge } from "@/typings/features/challenge";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'default-challenge-display',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
id: [ Number, String ]
|
||||
},
|
||||
computed: {
|
||||
challenge() {
|
||||
return layers[this.layer || this.tab.layer].challenges[this.id];
|
||||
},
|
||||
challengeDescription() {
|
||||
return coerceComponent(this.challenge.challengeDescription, 'div');
|
||||
},
|
||||
goalDescription() {
|
||||
if (this.challenge.goalDescription) {
|
||||
return coerceComponent(this.challenge.goalDescription);
|
||||
}
|
||||
return coerceComponent(`{{ format(${this.challenge.goal}) }} ${this.challenge.currencyDisplayName || 'points'}`);
|
||||
},
|
||||
rewardDescription() {
|
||||
return coerceComponent(this.challenge.rewardDescription);
|
||||
},
|
||||
rewardDisplay() {
|
||||
if (this.challenge.rewardDisplay) {
|
||||
return coerceComponent(`Currently: ${this.challenge.rewardDisplay}`);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "default-challenge-display",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
challenge(): Challenge {
|
||||
return layers[this.layer].challenges!.data[this.id];
|
||||
},
|
||||
challengeDescription(): Component | string {
|
||||
return coerceComponent(this.challenge.challengeDescription, "div");
|
||||
},
|
||||
goalDescription(): Component | string {
|
||||
if (this.challenge.goalDescription) {
|
||||
return coerceComponent(this.challenge.goalDescription);
|
||||
}
|
||||
return coerceComponent(
|
||||
`{{ format(${this.challenge.goal}) }} ${this.challenge.currencyDisplayName ||
|
||||
"points"}`
|
||||
);
|
||||
},
|
||||
rewardDescription(): Component | string {
|
||||
return coerceComponent(this.challenge.rewardDescription);
|
||||
},
|
||||
rewardDisplay(): Component | string | null {
|
||||
if (this.challenge.rewardDisplay) {
|
||||
return coerceComponent(`Currently: ${this.challenge.rewardDisplay}`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -1,77 +1,79 @@
|
|||
<template>
|
||||
<span>
|
||||
{{ resetDescription }}<b>{{ resetGain }}</b>
|
||||
{{ resource }}
|
||||
<br v-if="nextAt"/><br v-if="nextAt"/>
|
||||
{{ nextAt }}
|
||||
</span>
|
||||
<span>
|
||||
{{ resetDescription }}<b>{{ resetGain }}</b>
|
||||
{{ resource }}
|
||||
<br v-if="nextAt" /><br v-if="nextAt" />
|
||||
{{ nextAt }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import player from '../../game/player';
|
||||
import { format, formatWhole } from '../../util/bignum';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import Decimal, { format, formatWhole } from "@/util/bignum";
|
||||
import { InjectLayerMixin } from "@/util/vue";
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'default-prestige-button-display',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String
|
||||
},
|
||||
computed: {
|
||||
resetDescription() {
|
||||
if (player[this.layer || this.tab.layer].points.lt(1e3) || layers[this.layer || this.tab.layer].type === "static") {
|
||||
return layers[this.layer || this.tab.layer].resetDescription || "Reset for ";
|
||||
}
|
||||
return "";
|
||||
},
|
||||
resetGain() {
|
||||
return formatWhole(layers[this.layer || this.tab.layer].resetGain);
|
||||
},
|
||||
resource() {
|
||||
return layers[this.layer || this.tab.layer].resource;
|
||||
},
|
||||
showNextAt() {
|
||||
if (layers[this.layer || this.tab.layer].showNextAt != undefined) {
|
||||
return layers[this.layer || this.tab.layer].showNextAt;
|
||||
} else {
|
||||
return layers[this.layer || this.tab.layer].type === "static" ?
|
||||
player[this.layer || this.tab.layer].points.lt(30) : // static
|
||||
player[this.layer || this.tab.layer].points.lt(1e3) && layers[this.layer ||
|
||||
this.tab.layer].resetGain.lt(100); // normal
|
||||
}
|
||||
},
|
||||
nextAt() {
|
||||
if (this.showNextAt) {
|
||||
let prefix;
|
||||
if (layers[this.layer || this.tab.layer].type === "static") {
|
||||
if (layers[this.layer || this.tab.layer].baseAmount.gte(layers[this.layer || this.tab.layer].nextAt) &&
|
||||
layers[this.layer || this.tab.layer].canBuyMax !== false) {
|
||||
prefix = "Next:";
|
||||
} else {
|
||||
prefix = "Req:";
|
||||
}
|
||||
export default defineComponent({
|
||||
name: "default-prestige-button-display",
|
||||
mixins: [InjectLayerMixin],
|
||||
computed: {
|
||||
resetDescription(): string {
|
||||
if (player.layers[this.layer].points.lt(1e3) || layers[this.layer].type === "static") {
|
||||
return layers[this.layer].resetDescription || "Reset for ";
|
||||
}
|
||||
return "";
|
||||
},
|
||||
resetGain(): string {
|
||||
return formatWhole(layers[this.layer].resetGain);
|
||||
},
|
||||
resource(): string {
|
||||
return layers[this.layer].resource;
|
||||
},
|
||||
showNextAt(): boolean {
|
||||
if (layers[this.layer].showNextAt != undefined) {
|
||||
return layers[this.layer].showNextAt!;
|
||||
} else {
|
||||
return layers[this.layer].type === "static"
|
||||
? player.layers[this.layer].points.lt(30) // static
|
||||
: player.layers[this.layer].points.lt(1e3) &&
|
||||
layers[this.layer].resetGain.lt(100); // normal
|
||||
}
|
||||
},
|
||||
nextAt(): string {
|
||||
if (this.showNextAt) {
|
||||
let prefix;
|
||||
if (layers[this.layer].type === "static") {
|
||||
if (
|
||||
Decimal.gte(layers[this.layer].baseAmount!, layers[this.layer].nextAt) &&
|
||||
layers[this.layer].canBuyMax !== false
|
||||
) {
|
||||
prefix = "Next:";
|
||||
} else {
|
||||
prefix = "Req:";
|
||||
}
|
||||
|
||||
const baseAmount = formatWhole(layers[this.layer || this.tab.layer].baseAmount);
|
||||
const nextAt = (layers[this.layer || this.tab.layer].roundUpCost ? formatWhole : format)(layers[this.layer || this.tab.layer].nextAtMax);
|
||||
const baseResource = layers[this.layer || this.tab.layer].baseResource;
|
||||
const baseAmount = formatWhole(layers[this.layer].baseAmount!);
|
||||
const nextAt = (layers[this.layer].roundUpCost ? formatWhole : format)(
|
||||
layers[this.layer].nextAtMax
|
||||
);
|
||||
const baseResource = layers[this.layer].baseResource;
|
||||
|
||||
return `${prefix} ${baseAmount} / ${nextAt} ${baseResource}`;
|
||||
} else {
|
||||
let amount;
|
||||
if (layers[this.layer || this.tab.layer].roundUpCost) {
|
||||
amount = formatWhole(layers[this.layer || this.tab.layer].nextAt);
|
||||
} else {
|
||||
amount = format(layers[this.layer || this.tab.layer].nextAt);
|
||||
}
|
||||
return `Next at ${amount} ${layers[this.layer || this.tab.layer].baseResource}`;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
return `${prefix} ${baseAmount} / ${nextAt} ${baseResource}`;
|
||||
} else {
|
||||
let amount;
|
||||
if (layers[this.layer].roundUpCost) {
|
||||
amount = formatWhole(layers[this.layer].nextAt);
|
||||
} else {
|
||||
amount = format(layers[this.layer].nextAt);
|
||||
}
|
||||
return `Next at ${amount} ${layers[this.layer].baseResource}`;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -1,53 +1,56 @@
|
|||
<template>
|
||||
<span>
|
||||
<div v-if="title"><component :is="title" /></div>
|
||||
<component :is="description" />
|
||||
<div v-if="effectDisplay"><br>Currently: <component :is="effectDisplay" /></div>
|
||||
<br>
|
||||
Cost: {{ cost }} {{ costResource }}
|
||||
</span>
|
||||
<span>
|
||||
<div v-if="title"><component :is="title" /></div>
|
||||
<component :is="description" />
|
||||
<div v-if="effectDisplay"><br />Currently: <component :is="effectDisplay" /></div>
|
||||
<br />
|
||||
Cost: {{ cost }} {{ costResource }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
import { formatWhole } from '../../util/bignum';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { Upgrade } from "@/typings/features/upgrade";
|
||||
import { formatWhole } from "@/util/bignum";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'default-upgrade-display',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
id: [ Number, String ]
|
||||
},
|
||||
computed: {
|
||||
upgrade() {
|
||||
return layers[this.layer || this.tab.layer].upgrades[this.id];
|
||||
},
|
||||
title() {
|
||||
if (this.upgrade.title) {
|
||||
return coerceComponent(this.upgrade.title, 'h3');
|
||||
}
|
||||
return null;
|
||||
},
|
||||
description() {
|
||||
return coerceComponent(this.upgrade.description, 'div');
|
||||
},
|
||||
effectDisplay() {
|
||||
if (this.upgrade.effectDisplay) {
|
||||
return coerceComponent(this.upgrade.effectDisplay);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
cost() {
|
||||
return formatWhole(this.upgrade.cost);
|
||||
},
|
||||
costResource() {
|
||||
return this.upgrade.currencyDisplayName || layers[this.layer || this.tab.layer].resource;
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "default-upgrade-display",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
upgrade(): Upgrade {
|
||||
return layers[this.layer].upgrades!.data[this.id];
|
||||
},
|
||||
title(): Component | string | null {
|
||||
if (this.upgrade.title) {
|
||||
return coerceComponent(this.upgrade.title, "h3");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
description(): Component | string {
|
||||
return coerceComponent(this.upgrade.description, "div");
|
||||
},
|
||||
effectDisplay(): Component | string | null {
|
||||
if (this.upgrade.effectDisplay) {
|
||||
return coerceComponent(this.upgrade.effectDisplay);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
cost(): string {
|
||||
return formatWhole(this.upgrade.cost);
|
||||
},
|
||||
costResource(): string {
|
||||
return this.upgrade.currencyDisplayName || layers[this.layer].resource;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -1,30 +1,34 @@
|
|||
<template>
|
||||
<div v-if="grid" class="table">
|
||||
<div v-for="row in grid.rows" class="row" :key="row">
|
||||
<div v-for="col in grid.cols" :key="col">
|
||||
<gridable class="align" :id="id" :cell="row * 100 + col" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="grid" class="table">
|
||||
<div v-for="row in grid.rows" class="row" :key="row">
|
||||
<div v-for="col in grid.cols" :key="col">
|
||||
<grid-cell class="align" :id="id" :cell="row * 100 + col" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { Grid } from "@/typings/features/grid";
|
||||
import { InjectLayerMixin } from "@/util/vue";
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'grid',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
id: [ Number, String ]
|
||||
},
|
||||
computed: {
|
||||
grid() {
|
||||
return layers[this.layer || this.tab.layer].grids[this.id];
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "grid",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
grid(): Grid {
|
||||
return layers[this.layer].grids!.data[this.id];
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
97
src/components/features/GridCell.vue
Normal file
97
src/components/features/GridCell.vue
Normal file
|
@ -0,0 +1,97 @@
|
|||
<template>
|
||||
<button
|
||||
v-if="gridable.unlocked"
|
||||
:class="{ feature: true, tile: true, can: canClick, locked: !canClick }"
|
||||
:style="style"
|
||||
@click="gridable.click"
|
||||
@mousedown="start"
|
||||
@mouseleave="stop"
|
||||
@mouseup="stop"
|
||||
@touchstart="start"
|
||||
@touchend="stop"
|
||||
@touchcancel="stop"
|
||||
:disabled="!canClick"
|
||||
>
|
||||
<div v-if="title"><component :is="title" /></div>
|
||||
<component :is="display" style="white-space: pre-line;" />
|
||||
<branch-node :branches="gridable.branches" :id="id" featureType="gridable" />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { GridCell } from "@/typings/features/grid";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "grid-cell",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
},
|
||||
cell: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
},
|
||||
size: [Number, String]
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
interval: null,
|
||||
time: 0
|
||||
} as {
|
||||
interval: number | null;
|
||||
time: number;
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
gridCell(): GridCell {
|
||||
return layers[this.layer].grids!.data[this.id][this.cell] as GridCell;
|
||||
},
|
||||
canClick(): boolean {
|
||||
return this.gridCell.canClick;
|
||||
},
|
||||
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [
|
||||
this.canClick ? { backgroundColor: layers[this.layer].color } : {},
|
||||
layers[this.layer].componentStyles?.["grid-cell"],
|
||||
this.gridCell.style
|
||||
];
|
||||
},
|
||||
title(): Component | string | null {
|
||||
if (this.gridCell.title) {
|
||||
return coerceComponent(this.gridCell.title, "h3");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
display(): Component | string {
|
||||
return coerceComponent(this.gridCell.display, "div");
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
start() {
|
||||
if (!this.interval && this.gridCell.click) {
|
||||
this.interval = setInterval(this.gridCell.click, 250);
|
||||
}
|
||||
},
|
||||
stop() {
|
||||
if (this.interval) {
|
||||
clearInterval(this.interval);
|
||||
this.interval = null;
|
||||
this.time = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tile {
|
||||
min-height: 80px;
|
||||
width: 80px;
|
||||
font-size: 10px;
|
||||
}
|
||||
</style>
|
|
@ -1,77 +0,0 @@
|
|||
<template>
|
||||
<button v-if="gridable.unlocked" :class="{ feature: true, tile: true, can: canClick, locked: !canClick}"
|
||||
:style="style" @click="gridable.click" @mousedown="start" @mouseleave="stop" @mouseup="stop" @touchstart="start"
|
||||
@touchend="stop" @touchcancel="stop" :disabled="!canClick">
|
||||
<div v-if="title"><component :is="title" /></div>
|
||||
<component :is="display" style="white-space: pre-line;" />
|
||||
<branch-node :branches="gridable.branches" :id="id" featureType="gridable" />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
|
||||
export default {
|
||||
name: 'gridable',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
id: [ Number, String ],
|
||||
cell: [ Number, String ],
|
||||
size: {
|
||||
type: [ Number, String ]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
interval: false,
|
||||
time: 0
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
gridable() {
|
||||
return layers[this.layer || this.tab.layer].grids[this.id][this.cell];
|
||||
},
|
||||
canClick() {
|
||||
return this.gridable.canClick;
|
||||
},
|
||||
style() {
|
||||
return [
|
||||
this.canClick ? { 'background-color': layers[this.layer || this.tab.layer].color } : {},
|
||||
layers[this.layer || this.tab.layer].componentStyles?.gridable,
|
||||
this.gridable.style
|
||||
];
|
||||
},
|
||||
title() {
|
||||
if (this.gridable.title) {
|
||||
return coerceComponent(this.gridable.title, 'h3');
|
||||
}
|
||||
return null;
|
||||
},
|
||||
display() {
|
||||
return coerceComponent(this.gridable.display, 'div');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
start() {
|
||||
if (!this.interval) {
|
||||
this.interval = setInterval(this.gridable.click, 250);
|
||||
}
|
||||
},
|
||||
stop() {
|
||||
clearInterval(this.interval);
|
||||
this.interval = false;
|
||||
this.time = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tile {
|
||||
min-height: 80px;
|
||||
width: 80px;
|
||||
font-size: 10px;
|
||||
}
|
||||
</style>
|
|
@ -1,93 +1,96 @@
|
|||
<template>
|
||||
<div class="infobox" v-if="infobox.unlocked" :style="style" :class="{ collapsed, stacked }">
|
||||
<button class="title" :style="titleStyle" @click="toggle">
|
||||
<span class="toggle">▼</span>
|
||||
<component :is="title" />
|
||||
</button>
|
||||
<collapse-transition>
|
||||
<div v-if="!collapsed" class="body" :style="{ backgroundColor: borderColor }">
|
||||
<component :is="body" :style="bodyStyle" />
|
||||
</div>
|
||||
</collapse-transition>
|
||||
<branch-node :branches="infobox.branches" :id="id" featureType="infobox" />
|
||||
</div>
|
||||
<div class="infobox" v-if="infobox.unlocked" :style="style" :class="{ collapsed, stacked }">
|
||||
<button class="title" :style="titleStyle" @click="toggle">
|
||||
<span class="toggle">▼</span>
|
||||
<component :is="title" />
|
||||
</button>
|
||||
<collapse-transition>
|
||||
<div v-if="!collapsed" class="body" :style="{ backgroundColor: borderColor }">
|
||||
<component :is="body" :style="bodyStyle" />
|
||||
</div>
|
||||
</collapse-transition>
|
||||
<branch-node :branches="infobox.branches" :id="id" featureType="infobox" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import player from '../../game/player';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
import themes from '../../data/themes';
|
||||
<script lang="ts">
|
||||
import themes from "@/data/themes";
|
||||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { Infobox } from "@/typings/features/infobox";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'infobox',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
id: [ Number, String ]
|
||||
},
|
||||
computed: {
|
||||
infobox() {
|
||||
return layers[this.layer || this.tab.layer].infoboxes[this.id];
|
||||
},
|
||||
borderColor() {
|
||||
return this.infobox.borderColor || layers[this.layer || this.tab.layer].color;
|
||||
},
|
||||
style() {
|
||||
return [
|
||||
{ borderColor: this.borderColor },
|
||||
layers[this.layer || this.tab.layer].componentStyles?.infobox,
|
||||
this.infobox.style
|
||||
];
|
||||
},
|
||||
titleStyle() {
|
||||
return [
|
||||
{ backgroundColor: layers[this.layer || this.tab.layer].color },
|
||||
layers[this.layer || this.tab.layer].componentStyles?.['infobox-title'],
|
||||
this.infobox.titleStyle
|
||||
];
|
||||
},
|
||||
bodyStyle() {
|
||||
return [
|
||||
layers[this.layer || this.tab.layer].componentStyles?.['infobox-body'],
|
||||
this.infobox.bodyStyle
|
||||
];
|
||||
},
|
||||
title() {
|
||||
if (this.infobox.title) {
|
||||
return coerceComponent(this.infobox.title);
|
||||
}
|
||||
return coerceComponent(layers[this.layer || this.tab.layer].name);
|
||||
},
|
||||
body() {
|
||||
return coerceComponent(this.infobox.body);
|
||||
},
|
||||
collapsed() {
|
||||
return player[this.layer || this.tab.layer].infoboxes[this.id];
|
||||
},
|
||||
stacked() {
|
||||
return themes[player.theme].stackedInfoboxes;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
player[this.layer || this.tab.layer].infoboxes[this.id] = !player[this.layer || this.tab.layer].infoboxes[this.id];
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "infobox",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
infobox(): Infobox {
|
||||
return layers[this.layer].infoboxes!.data[this.id];
|
||||
},
|
||||
borderColor(): string {
|
||||
return this.infobox.borderColor || layers[this.layer].color;
|
||||
},
|
||||
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [
|
||||
{ borderColor: this.borderColor },
|
||||
layers[this.layer].componentStyles?.infobox,
|
||||
this.infobox.style
|
||||
];
|
||||
},
|
||||
titleStyle(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [
|
||||
{ backgroundColor: layers[this.layer].color },
|
||||
layers[this.layer].componentStyles?.["infobox-title"],
|
||||
this.infobox.titleStyle
|
||||
];
|
||||
},
|
||||
bodyStyle(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [layers[this.layer].componentStyles?.["infobox-body"], this.infobox.bodyStyle];
|
||||
},
|
||||
title(): Component | string {
|
||||
if (this.infobox.title) {
|
||||
return coerceComponent(this.infobox.title);
|
||||
}
|
||||
return coerceComponent(layers[this.layer].name || this.layer);
|
||||
},
|
||||
body(): Component | string {
|
||||
return coerceComponent(this.infobox.body);
|
||||
},
|
||||
collapsed(): boolean {
|
||||
return player.layers[this.layer].infoboxes[this.id];
|
||||
},
|
||||
stacked(): boolean {
|
||||
return themes[player.theme].stackedInfoboxes;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
player.layers[this.layer].infoboxes[this.id] = !player.layers[this.layer].infoboxes[
|
||||
this.id
|
||||
];
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.infobox {
|
||||
position: relative;
|
||||
width: 600px;
|
||||
max-width: 95%;
|
||||
margin-top: 0;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
width: 600px;
|
||||
max-width: 95%;
|
||||
margin-top: 0;
|
||||
text-align: left;
|
||||
border-style: solid;
|
||||
border-width: 0px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.infobox.stacked {
|
||||
|
@ -95,27 +98,27 @@ export default {
|
|||
}
|
||||
|
||||
.infobox:not(.stacked) + .infobox:not(.stacked) {
|
||||
margin-top: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.infobox + :not(.infobox) {
|
||||
margin-top: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 24px;
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
padding: 4px;
|
||||
width: auto;
|
||||
text-align: left;
|
||||
padding-left: 30px;
|
||||
font-size: 24px;
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
padding: 4px;
|
||||
width: auto;
|
||||
text-align: left;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.infobox:not(.stacked) .title {
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
}
|
||||
|
||||
.infobox.stacked + .infobox.stacked {
|
||||
|
@ -125,32 +128,32 @@ export default {
|
|||
}
|
||||
|
||||
.stacked .title {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.collapsed:not(.stacked) .title::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 4px;
|
||||
background-color: inherit;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.collapsed .toggle {
|
||||
transform: rotate(-90deg);
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.body {
|
||||
transition-duration: .5s;
|
||||
border-radius: 5px;
|
||||
border-top-left-radius: 0;
|
||||
transition-duration: 0.5s;
|
||||
border-radius: 5px;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
.infobox:not(.stacked) .body {
|
||||
|
@ -158,12 +161,12 @@ export default {
|
|||
}
|
||||
|
||||
.body > * {
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
border-top-left-radius: 0;
|
||||
background-color: var(--background);
|
||||
border-radius: 5px;
|
||||
border-top-left-radius: 0;
|
||||
background-color: var(--background);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,50 +1,53 @@
|
|||
<template>
|
||||
<div>
|
||||
<span v-if="showPrefix">You have </span>
|
||||
<resource :amount="amount" :color="color" />
|
||||
{{ resource }}<!-- remove whitespace -->
|
||||
<span v-if="effectDisplay">, <component :is="effectDisplay" /></span>
|
||||
<br><br>
|
||||
</div>
|
||||
<div>
|
||||
<span v-if="showPrefix">You have </span>
|
||||
<resource :amount="amount" :color="color" />
|
||||
{{ resource
|
||||
}}<!-- remove whitespace -->
|
||||
<span v-if="effectDisplay">, <component :is="effectDisplay"/></span>
|
||||
<br /><br />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import player from '../../game/player';
|
||||
import { layers } from '../../game/layers';
|
||||
import { format, formatWhole } from '../../util/bignum';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { format, formatWhole } from "@/util/bignum";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'main-display',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
precision: Number
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
return layers[this.layer || this.tab.layer].componentStyles?.['main-display'];
|
||||
},
|
||||
resource() {
|
||||
return layers[this.layer || this.tab.layer].resource;
|
||||
},
|
||||
effectDisplay() {
|
||||
return coerceComponent(layers[this.layer || this.tab.layer].effectDisplay);
|
||||
},
|
||||
showPrefix() {
|
||||
return player[this.layer || this.tab.layer].points.lt('1e1000');
|
||||
},
|
||||
color() {
|
||||
return layers[this.layer || this.tab.layer].color;
|
||||
},
|
||||
amount() {
|
||||
return this.precision == undefined ?
|
||||
formatWhole(player[this.layer || this.tab.layer].points) :
|
||||
format(player[this.layer || this.tab.layer].points, this.precision);
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "main-display",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
precision: Number
|
||||
},
|
||||
computed: {
|
||||
style(): Partial<CSSStyleDeclaration> | undefined {
|
||||
return layers[this.layer].componentStyles?.["main-display"];
|
||||
},
|
||||
resource(): string {
|
||||
return layers[this.layer].resource;
|
||||
},
|
||||
effectDisplay(): Component | string | undefined {
|
||||
return (
|
||||
layers[this.layer].effectDisplay &&
|
||||
coerceComponent(layers[this.layer].effectDisplay!)
|
||||
);
|
||||
},
|
||||
showPrefix(): boolean {
|
||||
return player.layers[this.layer].points.lt("1e1000");
|
||||
},
|
||||
color(): string {
|
||||
return layers[this.layer].color;
|
||||
},
|
||||
amount(): string {
|
||||
return this.precision == undefined
|
||||
? formatWhole(player.layers[this.layer].points)
|
||||
: format(player.layers[this.layer].points, this.precision);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -1,28 +1,30 @@
|
|||
<template>
|
||||
<div v-if="mark">
|
||||
<div v-if="mark === true" class="mark star"></div>
|
||||
<img v-else class="mark" :src="mark" />
|
||||
</div>
|
||||
<div v-if="mark">
|
||||
<div v-if="mark === true" class="mark star"></div>
|
||||
<img v-else class="mark" :src="mark" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'mark-node',
|
||||
props: {
|
||||
mark: [ Boolean, String ]
|
||||
}
|
||||
};
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "mark-node",
|
||||
props: {
|
||||
mark: [Boolean, String]
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mark {
|
||||
position: absolute;
|
||||
left: -25px;
|
||||
top: -10px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
left: -25px;
|
||||
top: -10px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
margin-left: 0.9em;
|
||||
margin-right: 0.9em;
|
||||
margin-bottom: 1.2em;
|
||||
|
@ -33,34 +35,34 @@ export default {
|
|||
}
|
||||
|
||||
.star {
|
||||
left: -10px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 0.9em;
|
||||
margin-right: 0.9em;
|
||||
margin-bottom: 1.2em;
|
||||
border-right: 0.3em solid transparent;
|
||||
border-bottom: 0.7em solid #ffcc00;
|
||||
border-left: 0.3em solid transparent;
|
||||
font-size: 10px;
|
||||
pointer-events: none;
|
||||
left: -10px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 0.9em;
|
||||
margin-right: 0.9em;
|
||||
margin-bottom: 1.2em;
|
||||
border-right: 0.3em solid transparent;
|
||||
border-bottom: 0.7em solid #ffcc00;
|
||||
border-left: 0.3em solid transparent;
|
||||
font-size: 10px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.star::before,
|
||||
.star::after {
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: .6em;
|
||||
left: -1em;
|
||||
border-right: 1em solid transparent;
|
||||
border-bottom: 0.7em solid #ffcc00;
|
||||
border-left: 1em solid transparent;
|
||||
transform: rotate(-35deg);
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 0.6em;
|
||||
left: -1em;
|
||||
border-right: 1em solid transparent;
|
||||
border-bottom: 0.7em solid #ffcc00;
|
||||
border-left: 1em solid transparent;
|
||||
transform: rotate(-35deg);
|
||||
}
|
||||
|
||||
.star::after {
|
||||
transform: rotate(35deg);
|
||||
transform: rotate(35deg);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,48 +1,50 @@
|
|||
<template>
|
||||
<button @click="press" :class="{ feature: true, can: unlocked, locked: !unlocked }" :style="style">
|
||||
<component :is="masterButtonDisplay" />
|
||||
</button>
|
||||
<button
|
||||
@click="press"
|
||||
:class="{ feature: true, can: unlocked, locked: !unlocked }"
|
||||
:style="style"
|
||||
>
|
||||
<component :is="masterButtonDisplay" />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import player from '../../game/player';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { CoercableComponent } from "@/typings/component";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent, PropType } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'master-button',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
display: [ String, Object ]
|
||||
},
|
||||
emits: [ 'press' ],
|
||||
computed: {
|
||||
style() {
|
||||
return [
|
||||
layers[this.layer || this.tab.layer].componentStyles?.['master-button']
|
||||
];
|
||||
},
|
||||
unlocked() {
|
||||
return player[this.layer || this.tab.layer].unlocked;
|
||||
},
|
||||
masterButtonDisplay() {
|
||||
if (this.display) {
|
||||
return coerceComponent(this.display);
|
||||
}
|
||||
if (layers[this.layer || this.tab.layer].clickables?.masterButtonDisplay) {
|
||||
return coerceComponent(layers[this.layer || this.tab.layer].clickables?.masterButtonDisplay);
|
||||
}
|
||||
return coerceComponent("Click Me!");
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
press() {
|
||||
this.$emit("press");
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "master-button",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
display: [String, Object] as PropType<CoercableComponent>
|
||||
},
|
||||
emits: ["press"],
|
||||
computed: {
|
||||
style(): Partial<CSSStyleDeclaration> | undefined {
|
||||
return layers[this.layer].componentStyles?.["master-button"];
|
||||
},
|
||||
unlocked(): boolean {
|
||||
return player.layers[this.layer].unlocked;
|
||||
},
|
||||
masterButtonDisplay(): Component | string {
|
||||
if (this.display) {
|
||||
return coerceComponent(this.display);
|
||||
}
|
||||
if (layers[this.layer].clickables?.masterButtonDisplay) {
|
||||
return coerceComponent(layers[this.layer].clickables!.masterButtonDisplay!);
|
||||
}
|
||||
return coerceComponent("Click Me!");
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
press() {
|
||||
this.$emit("press");
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -1,53 +1,58 @@
|
|||
<template>
|
||||
<div v-if="milestone.shown" :style="style" :class="{ feature: true, milestone: true, done: milestone.earned }">
|
||||
<div v-if="requirementDisplay"><component :is="requirementDisplay" /></div>
|
||||
<div v-if="effectDisplay"><component :is="effectDisplay" /></div>
|
||||
<component v-if="optionsDisplay" :is="optionsDisplay" />
|
||||
<branch-node :branches="milestone.branches" :id="id" featureType="milestone" />
|
||||
</div>
|
||||
<div
|
||||
v-if="milestone.shown"
|
||||
:style="style"
|
||||
:class="{ feature: true, milestone: true, done: milestone.earned }"
|
||||
>
|
||||
<div v-if="requirementDisplay"><component :is="requirementDisplay" /></div>
|
||||
<div v-if="effectDisplay"><component :is="effectDisplay" /></div>
|
||||
<component v-if="optionsDisplay" :is="optionsDisplay" />
|
||||
<branch-node :branches="milestone.branches" :id="id" featureType="milestone" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { Milestone } from "@/typings/features/milestone";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'milestone',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
id: [ Number, String ]
|
||||
},
|
||||
computed: {
|
||||
milestone() {
|
||||
return layers[this.layer || this.tab.layer].milestones[this.id];
|
||||
},
|
||||
style() {
|
||||
return [
|
||||
layers[this.layer || this.tab.layer].componentStyles?.milestone,
|
||||
this.milestone.style
|
||||
];
|
||||
},
|
||||
requirementDisplay() {
|
||||
if (this.milestone.requirementDisplay) {
|
||||
return coerceComponent(this.milestone.requirementDisplay, 'h3');
|
||||
}
|
||||
return null;
|
||||
},
|
||||
effectDisplay() {
|
||||
if (this.milestone.effectDisplay) {
|
||||
return coerceComponent(this.milestone.effectDisplay, 'b');
|
||||
}
|
||||
return null;
|
||||
},
|
||||
optionsDisplay() {
|
||||
if (this.milestone.optionsDisplay && this.milestone.earned) {
|
||||
return coerceComponent(this.milestone.optionsDisplay, 'div');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "milestone",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
milestone(): Milestone {
|
||||
return layers[this.layer].milestones!.data[this.id];
|
||||
},
|
||||
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [layers[this.layer].componentStyles?.milestone, this.milestone.style];
|
||||
},
|
||||
requirementDisplay(): Component | string | null {
|
||||
if (this.milestone.requirementDisplay) {
|
||||
return coerceComponent(this.milestone.requirementDisplay, "h3");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
effectDisplay(): Component | string | null {
|
||||
if (this.milestone.effectDisplay) {
|
||||
return coerceComponent(this.milestone.effectDisplay, "b");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
optionsDisplay(): Component | string | null {
|
||||
if (this.milestone.optionsDisplay && this.milestone.earned) {
|
||||
return coerceComponent(this.milestone.optionsDisplay, "div");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -1,27 +1,32 @@
|
|||
<template>
|
||||
<div v-if="filteredMilestones" class="table">
|
||||
<milestone v-for="(milestone, id) in filteredMilestones" :key="id" :id="id" />
|
||||
</div>
|
||||
<div v-if="filteredMilestones" class="table">
|
||||
<milestone v-for="(milestone, id) in filteredMilestones" :key="id" :id="id" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { getFiltered } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { Milestone } from "@/typings/features/milestone";
|
||||
import { getFiltered, InjectLayerMixin } from "@/util/vue";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'milestones',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
milestones: Array
|
||||
},
|
||||
computed: {
|
||||
filteredMilestones() {
|
||||
return getFiltered(layers[this.layer || this.tab.layer].milestones, this.milestones);
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "milestones",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
milestones: {
|
||||
type: Object as PropType<Array<string>>
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredMilestones(): Record<string, Milestone> {
|
||||
if (layers[this.layer].milestones) {
|
||||
return getFiltered<Milestone>(layers[this.layer].milestones!.data, this.milestones);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -1,48 +1,49 @@
|
|||
<template>
|
||||
<button :style="style" @click="resetLayer"
|
||||
:class="{ [layer || tab.layer]: true, reset: true, locked: !canReset, can: canReset }" >
|
||||
<component v-if="prestigeButtonDisplay" :is="prestigeButtonDisplay" />
|
||||
<default-prestige-button-display v-else />
|
||||
</button>
|
||||
<button
|
||||
:style="style"
|
||||
@click="resetLayer"
|
||||
:class="{ [layer]: true, reset: true, locked: !canReset, can: canReset }"
|
||||
>
|
||||
<component v-if="prestigeButtonDisplay" :is="prestigeButtonDisplay" />
|
||||
<default-prestige-button-display v-else />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { resetLayer } from '../../util/layers';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { resetLayer } from "@/util/layers";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'prestige-button',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String
|
||||
},
|
||||
computed: {
|
||||
canReset() {
|
||||
return layers[this.layer || this.tab.layer].canReset;
|
||||
},
|
||||
color() {
|
||||
return layers[this.layer || this.tab.layer].color;
|
||||
},
|
||||
prestigeButtonDisplay() {
|
||||
if (layers[this.layer || this.tab.layer].prestigeButtonDisplay) {
|
||||
return coerceComponent(layers[this.layer || this.tab.layer].prestigeButtonDisplay);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
style() {
|
||||
return [
|
||||
this.canReset ? { 'background-color': this.color } : {},
|
||||
layers[this.layer || this.tab.layer].componentStyles?.['prestige-button']
|
||||
];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetLayer() {
|
||||
resetLayer(this.layer || this.tab.layer);
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "prestige-button",
|
||||
mixins: [InjectLayerMixin],
|
||||
computed: {
|
||||
canReset(): boolean {
|
||||
return layers[this.layer].canReset;
|
||||
},
|
||||
color(): string {
|
||||
return layers[this.layer].color;
|
||||
},
|
||||
prestigeButtonDisplay(): Component | string | null {
|
||||
if (layers[this.layer].prestigeButtonDisplay) {
|
||||
return coerceComponent(layers[this.layer].prestigeButtonDisplay!);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [
|
||||
this.canReset ? { backgroundColor: this.color } : undefined,
|
||||
layers[this.layer].componentStyles?.["prestige-button"]
|
||||
];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetLayer() {
|
||||
resetLayer(this.layer);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -1,55 +1,76 @@
|
|||
<template>
|
||||
<div class="resource-display" :class="{ empty }">
|
||||
<div v-if="baseAmount != undefined">You have {{ baseAmount }} {{ baseResource }}</div>
|
||||
<div v-if="passiveGeneration != undefined">You are gaining {{ passiveGeneration }} {{ resource }} per second</div>
|
||||
<spacer v-if="(baseAmount != undefined || passiveGeneration != undefined) && (best != undefined || total != undefined)" />
|
||||
<div v-if="best != undefined">Your best {{ resource }} is {{ best }}</div>
|
||||
<div v-if="total != undefined">You have made a total of {{ total }} {{ resource }}</div>
|
||||
</div>
|
||||
<div class="resource-display" :class="{ empty }">
|
||||
<div v-if="baseAmount != undefined && baseResource != undefined">
|
||||
You have {{ baseAmount }} {{ baseResource }}
|
||||
</div>
|
||||
<div v-if="passiveGeneration != undefined">
|
||||
You are gaining {{ passiveGeneration }} {{ resource }} per second
|
||||
</div>
|
||||
<spacer
|
||||
v-if="
|
||||
(baseAmount != undefined || passiveGeneration != undefined) &&
|
||||
(best != undefined || total != undefined)
|
||||
"
|
||||
/>
|
||||
<div v-if="best != undefined">Your best {{ resource }} is {{ best }}</div>
|
||||
<div v-if="total != undefined">You have made a total of {{ total }} {{ resource }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import player from '../../game/player';
|
||||
import Decimal, { formatWhole } from '../../util/bignum';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { DecimalSource } from "@/lib/break_eternity";
|
||||
import Decimal, { formatWhole } from "@/util/bignum";
|
||||
import { InjectLayerMixin } from "@/util/vue";
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'resource-display',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String
|
||||
},
|
||||
computed: {
|
||||
baseAmount() {
|
||||
return layers[this.layer || this.tab.layer].baseAmount ? formatWhole(layers[this.layer || this.tab.layer].baseAmount) : null;
|
||||
},
|
||||
baseResource() {
|
||||
return layers[this.layer || this.tab.layer].baseResource;
|
||||
},
|
||||
passiveGeneration() {
|
||||
return layers[this.layer || this.tab.layer].passiveGeneration ?
|
||||
formatWhole(Decimal.times(layers[this.layer || this.tab.layer].resetGain,
|
||||
layers[this.layer || this.tab.layer].passiveGeneration)) :
|
||||
null;
|
||||
},
|
||||
resource() {
|
||||
return layers[this.layer || this.tab.layer].resource;
|
||||
},
|
||||
best() {
|
||||
return player[this.layer || this.tab.layer].best ? formatWhole(player[this.layer || this.tab.layer].best) : null;
|
||||
},
|
||||
total() {
|
||||
return player[this.layer || this.tab.layer].total ? formatWhole(player[this.layer || this.tab.layer].total) : null;
|
||||
},
|
||||
empty() {
|
||||
return !(this.baseAmount || this.passiveGeneration || this.best || this.total);
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "resource-display",
|
||||
mixins: [InjectLayerMixin],
|
||||
computed: {
|
||||
baseAmount(): string | null {
|
||||
return layers[this.layer].baseAmount
|
||||
? formatWhole(layers[this.layer].baseAmount!)
|
||||
: null;
|
||||
},
|
||||
baseResource(): string | undefined {
|
||||
return layers[this.layer].baseResource;
|
||||
},
|
||||
passiveGeneration(): string | null {
|
||||
return layers[this.layer].passiveGeneration
|
||||
? formatWhole(
|
||||
Decimal.times(
|
||||
layers[this.layer].resetGain,
|
||||
layers[this.layer].passiveGeneration === true
|
||||
? 1
|
||||
: (layers[this.layer].passiveGeneration as DecimalSource)
|
||||
)
|
||||
)
|
||||
: null;
|
||||
},
|
||||
resource(): string {
|
||||
return layers[this.layer].resource;
|
||||
},
|
||||
best(): string | null {
|
||||
return player.layers[this.layer].best
|
||||
? formatWhole(player.layers[this.layer].best as Decimal)
|
||||
: null;
|
||||
},
|
||||
total(): string | null {
|
||||
return player.layers[this.layer].total
|
||||
? formatWhole(player.layers[this.layer].total as Decimal)
|
||||
: null;
|
||||
},
|
||||
empty(): boolean {
|
||||
return !(this.baseAmount || this.passiveGeneration || this.best || this.total);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.resource-display:not(.empty) {
|
||||
margin: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
</style>
|
|
@ -1,90 +1,102 @@
|
|||
<template>
|
||||
<div style="display: flex">
|
||||
<tooltip display="Disable respec confirmation">
|
||||
<Toggle :value="confirmRespec" @change="setConfirmRespec" />
|
||||
</tooltip>
|
||||
<button @click="respec" :class="{ feature: true, respec: true, can: unlocked, locked: !unlocked }"
|
||||
style="margin-right: 18px" :style="style">
|
||||
<component :is="respecButtonDisplay" />
|
||||
</button>
|
||||
<Modal :show="confirming" @close="cancel">
|
||||
<template v-slot:header>
|
||||
<h2>Confirm Respec</h2>
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<slot name="respec-warning">
|
||||
<div>Are you sure you want to respec? This will force you to do a {{ name }} respec as well!</div>
|
||||
</slot>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<div class="modal-footer">
|
||||
<div class="modal-flex-grow"></div>
|
||||
<danger-button class="button modal-button" @click="confirm" skipConfirm>Yes</danger-button>
|
||||
<button class="button modal-button" @click="cancel">Cancel</button>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</div>
|
||||
<div style="display: flex">
|
||||
<tooltip display="Disable respec confirmation">
|
||||
<Toggle :value="confirmRespec" @change="setConfirmRespec" />
|
||||
</tooltip>
|
||||
<button
|
||||
@click="respec"
|
||||
:class="{ feature: true, respec: true, can: unlocked, locked: !unlocked }"
|
||||
style="margin-right: 18px"
|
||||
:style="style"
|
||||
>
|
||||
<component :is="respecButtonDisplay" />
|
||||
</button>
|
||||
<Modal :show="confirming" @close="cancel">
|
||||
<template v-slot:header>
|
||||
<h2>Confirm Respec</h2>
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<slot name="respec-warning">
|
||||
<component :is="respecWarning" />
|
||||
</slot>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<div class="modal-footer">
|
||||
<div class="modal-flex-grow"></div>
|
||||
<danger-button class="button modal-button" @click="confirm" skipConfirm
|
||||
>Yes</danger-button
|
||||
>
|
||||
<button class="button modal-button" @click="cancel">Cancel</button>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import player from '../../game/player';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { CoercableComponent } from "@/typings/component";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent, PropType } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'respec-button',
|
||||
inject: [ 'tab' ],
|
||||
data() {
|
||||
return {
|
||||
confirming: false
|
||||
};
|
||||
},
|
||||
props: {
|
||||
layer: String,
|
||||
confirmRespec: Boolean,
|
||||
display: [ String, Object ]
|
||||
},
|
||||
emits: [ 'set-confirm-respec', 'respec' ],
|
||||
computed: {
|
||||
style() {
|
||||
return [
|
||||
layers[this.layer || this.tab.layer].componentStyles?.['respec-button']
|
||||
];
|
||||
},
|
||||
unlocked() {
|
||||
return player[this.layer || this.tab.layer].unlocked;
|
||||
},
|
||||
respecButtonDisplay() {
|
||||
if (this.display) {
|
||||
return coerceComponent(this.display);
|
||||
}
|
||||
return coerceComponent("Respec");
|
||||
},
|
||||
name() {
|
||||
return layers[this.layer || this.tab.layer].name || this.layer || this.tab.layer;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setConfirmRespec(value) {
|
||||
this.$emit("set-confirm-respec", value);
|
||||
},
|
||||
respec() {
|
||||
if (this.confirmRespec) {
|
||||
this.confirming = true;
|
||||
} else {
|
||||
this.$emit("respec");
|
||||
}
|
||||
},
|
||||
confirm() {
|
||||
this.$emit("respec");
|
||||
this.confirming = false;
|
||||
},
|
||||
cancel() {
|
||||
this.confirming = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "respec-button",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
confirmRespec: Boolean,
|
||||
display: [String, Object] as PropType<CoercableComponent>,
|
||||
respecWarningDisplay: [String, Object] as PropType<CoercableComponent>
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
confirming: false
|
||||
};
|
||||
},
|
||||
emits: ["set-confirm-respec", "respec"],
|
||||
computed: {
|
||||
style(): Partial<CSSStyleDeclaration> | undefined {
|
||||
return layers[this.layer].componentStyles?.["respec-button"];
|
||||
},
|
||||
unlocked(): boolean {
|
||||
return player.layers[this.layer].unlocked;
|
||||
},
|
||||
respecButtonDisplay(): Component | string {
|
||||
if (this.display) {
|
||||
return coerceComponent(this.display);
|
||||
}
|
||||
return coerceComponent("Respec");
|
||||
},
|
||||
respecWarning(): Component | string {
|
||||
if (this.respecWarningDisplay) {
|
||||
return coerceComponent(this.respecWarningDisplay);
|
||||
}
|
||||
return coerceComponent(
|
||||
`Are you sure you want to respec? This will force you to do a ${layers[this.layer]
|
||||
.name || this.layer} respec as well!`
|
||||
);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setConfirmRespec(value: boolean) {
|
||||
this.$emit("set-confirm-respec", value);
|
||||
},
|
||||
respec() {
|
||||
if (this.confirmRespec) {
|
||||
this.confirming = true;
|
||||
} else {
|
||||
this.$emit("respec");
|
||||
}
|
||||
},
|
||||
confirm() {
|
||||
this.$emit("respec");
|
||||
this.confirming = false;
|
||||
},
|
||||
cancel() {
|
||||
this.confirming = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -96,14 +108,14 @@ export default {
|
|||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.modal-flex-grow {
|
||||
flex-grow: 1;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.modal-button {
|
||||
margin-left: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
32
src/components/features/Subtab.vue
Normal file
32
src/components/features/Subtab.vue
Normal file
|
@ -0,0 +1,32 @@
|
|||
<template>
|
||||
<LayerProvider :layer="layer" :index="tab.index">
|
||||
<component :is="display" />
|
||||
</LayerProvider>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "subtab",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
display(): Component | string | undefined {
|
||||
return (
|
||||
layers[this.layer].subtabs![this.id].display &&
|
||||
coerceComponent(layers[this.layer].subtabs![this.id].display!)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -1,54 +1,65 @@
|
|||
<template>
|
||||
<button v-if="upgrade.unlocked" :style="style" @click="buy"
|
||||
:class="{
|
||||
feature: true,
|
||||
[tab.layer]: true,
|
||||
upgrade: true,
|
||||
can: upgrade.canAfford && !upgrade.bought,
|
||||
locked: !upgrade.canAfford && !upgrade.bought,
|
||||
bought: upgrade.bought
|
||||
}" :disabled="!upgrade.canAfford && !upgrade.bought">
|
||||
<component v-if="fullDisplay" :is="fullDisplay" />
|
||||
<default-upgrade-display v-else :id="id" />
|
||||
<branch-node :branches="upgrade.branches" :id="id" featureType="upgrade" />
|
||||
</button>
|
||||
<button
|
||||
v-if="upgrade.unlocked"
|
||||
:style="style"
|
||||
@click="buy"
|
||||
:class="{
|
||||
feature: true,
|
||||
[layer]: true,
|
||||
upgrade: true,
|
||||
can: upgrade.canAfford && !upgrade.bought,
|
||||
locked: !upgrade.canAfford && !upgrade.bought,
|
||||
bought: upgrade.bought
|
||||
}"
|
||||
:disabled="!upgrade.canAfford && !upgrade.bought"
|
||||
>
|
||||
<component v-if="fullDisplay" :is="fullDisplay" />
|
||||
<default-upgrade-display v-else :id="id" />
|
||||
<branch-node :branches="upgrade.branches" :id="id" featureType="upgrade" />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { Upgrade } from "@/typings/features/upgrade";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'upgrade',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
id: [ Number, String ]
|
||||
},
|
||||
computed: {
|
||||
upgrade() {
|
||||
return layers[this.layer || this.tab.layer].upgrades[this.id];
|
||||
},
|
||||
style() {
|
||||
return [
|
||||
this.upgrade.canAfford && !this.upgrade.bought ? { 'background-color': layers[this.layer || this.tab.layer].color } : {},
|
||||
layers[this.layer || this.tab.layer].componentStyles?.upgrade,
|
||||
this.upgrade.style
|
||||
];
|
||||
},
|
||||
fullDisplay() {
|
||||
if (this.upgrade.fullDisplay) {
|
||||
return coerceComponent(this.upgrade.fullDisplay, 'div');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
buy() {
|
||||
this.upgrade.buy();
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "upgrade",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
upgrade(): Upgrade {
|
||||
return layers[this.layer].upgrades!.data[this.id];
|
||||
},
|
||||
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [
|
||||
this.upgrade.canAfford && !this.upgrade.bought
|
||||
? { backgroundColor: layers[this.layer].color }
|
||||
: undefined,
|
||||
layers[this.layer].componentStyles?.upgrade,
|
||||
this.upgrade.style
|
||||
];
|
||||
},
|
||||
fullDisplay(): Component | string | null {
|
||||
if (this.upgrade.fullDisplay) {
|
||||
return coerceComponent(this.upgrade.fullDisplay, "div");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
buy() {
|
||||
this.upgrade.buy();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -1,36 +1,45 @@
|
|||
<template>
|
||||
<div v-if="filteredUpgrades" class="table">
|
||||
<template v-if="filteredUpgrades.rows && filteredUpgrades.cols">
|
||||
<div v-for="row in filteredUpgrades.rows" class="row" :key="row">
|
||||
<div v-for="col in filteredUpgrades.cols" :key="col">
|
||||
<upgrade v-if="filteredUpgrades[row * 10 + col] !== undefined" class="align" :id="row * 10 + col" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<row v-else>
|
||||
<upgrade v-for="(upgrade, id) in filteredUpgrades" :key="id" class="align" :id="id" />
|
||||
</row>
|
||||
</div>
|
||||
<div v-if="filteredUpgrades" class="table">
|
||||
<template v-if="filteredUpgrades.rows && filteredUpgrades.cols">
|
||||
<div v-for="row in filteredUpgrades.rows" class="row" :key="row">
|
||||
<div v-for="col in filteredUpgrades.cols" :key="col">
|
||||
<upgrade
|
||||
v-if="filteredUpgrades[row * 10 + col] !== undefined"
|
||||
class="align"
|
||||
:id="row * 10 + col"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<row v-else>
|
||||
<upgrade v-for="(upgrade, id) in filteredUpgrades" :key="id" class="align" :id="id" />
|
||||
</row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { getFiltered } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { Upgrade } from "@/typings/features/upgrade";
|
||||
import { getFiltered, InjectLayerMixin } from "@/util/vue";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'upgrades',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
upgrades: Array
|
||||
},
|
||||
computed: {
|
||||
filteredUpgrades() {
|
||||
return getFiltered(layers[this.layer || this.tab.layer].upgrades, this.upgrades);
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "upgrades",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
upgrades: {
|
||||
type: Object as PropType<Array<string>>
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredUpgrades(): Record<string, Upgrade> {
|
||||
if (layers[this.layer].upgrades) {
|
||||
return getFiltered<Upgrade>(layers[this.layer].upgrades!.data, this.upgrades);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -1,76 +1,78 @@
|
|||
<template>
|
||||
<span class="container" :class="{ confirming }">
|
||||
<span v-if="confirming">Are you sure?</span>
|
||||
<button @click.stop="click" class="button danger" :disabled="disabled">
|
||||
<span v-if="confirming">Yes</span>
|
||||
<slot v-else />
|
||||
</button>
|
||||
<button v-if="confirming" class="button" @click.stop="cancel">No</button>
|
||||
</span>
|
||||
<span class="container" :class="{ confirming }">
|
||||
<span v-if="confirming">Are you sure?</span>
|
||||
<button @click.stop="click" class="button danger" :disabled="disabled">
|
||||
<span v-if="confirming">Yes</span>
|
||||
<slot v-else />
|
||||
</button>
|
||||
<button v-if="confirming" class="button" @click.stop="cancel">No</button>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'danger-button',
|
||||
data() {
|
||||
return {
|
||||
confirming: false
|
||||
}
|
||||
},
|
||||
props: {
|
||||
disabled: Boolean,
|
||||
skipConfirm: Boolean
|
||||
},
|
||||
emits: [ 'click', 'confirmingChanged' ],
|
||||
watch: {
|
||||
confirming(newValue) {
|
||||
this.$emit('confirmingChanged', newValue);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
click() {
|
||||
if (this.skipConfirm) {
|
||||
this.$emit('click');
|
||||
return;
|
||||
}
|
||||
if (this.confirming) {
|
||||
this.$emit('click');
|
||||
}
|
||||
this.confirming = !this.confirming;
|
||||
},
|
||||
cancel() {
|
||||
this.confirming = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "danger-button",
|
||||
data() {
|
||||
return {
|
||||
confirming: false
|
||||
};
|
||||
},
|
||||
props: {
|
||||
disabled: Boolean,
|
||||
skipConfirm: Boolean
|
||||
},
|
||||
emits: ["click", "confirmingChanged"],
|
||||
watch: {
|
||||
confirming(newValue) {
|
||||
this.$emit("confirmingChanged", newValue);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
click() {
|
||||
if (this.skipConfirm) {
|
||||
this.$emit("click");
|
||||
return;
|
||||
}
|
||||
if (this.confirming) {
|
||||
this.$emit("click");
|
||||
}
|
||||
this.confirming = !this.confirming;
|
||||
},
|
||||
cancel() {
|
||||
this.confirming = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.container.confirming button {
|
||||
font-size: 1em;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.container > * {
|
||||
margin: 0 4px;
|
||||
margin: 0 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.danger {
|
||||
position: relative;
|
||||
border: solid 2px var(--danger);
|
||||
border: solid 2px var(--danger);
|
||||
border-right-width: 16px;
|
||||
}
|
||||
|
||||
.danger::after {
|
||||
position: absolute;
|
||||
content: "!";
|
||||
color: white;
|
||||
content: "!";
|
||||
color: white;
|
||||
right: -13px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,75 +1,82 @@
|
|||
<template>
|
||||
<button @click.stop="click" class="feedback" :class="{ activated, left }"><slot /></button>
|
||||
<button @click.stop="click" class="feedback" :class="{ activated, left }">
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'feedback-button',
|
||||
data() {
|
||||
return {
|
||||
activated: false,
|
||||
activatedTimeout: null
|
||||
}
|
||||
},
|
||||
props: {
|
||||
left: Boolean
|
||||
},
|
||||
emits: [ 'click' ],
|
||||
methods: {
|
||||
click() {
|
||||
this.$emit('click');
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
// Give feedback to user
|
||||
if (this.activatedTimeout) {
|
||||
clearTimeout(this.activatedTimeout);
|
||||
}
|
||||
this.activated = false;
|
||||
this.$nextTick(() => {
|
||||
this.activated = true;
|
||||
this.activatedTimeout = setTimeout(() => this.activated = false, 500);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "feedback-button",
|
||||
data() {
|
||||
return {
|
||||
activated: false,
|
||||
activatedTimeout: null
|
||||
} as {
|
||||
activated: boolean;
|
||||
activatedTimeout: number | null;
|
||||
};
|
||||
},
|
||||
props: {
|
||||
left: Boolean
|
||||
},
|
||||
emits: ["click"],
|
||||
methods: {
|
||||
click() {
|
||||
this.$emit("click");
|
||||
|
||||
// Give feedback to user
|
||||
if (this.activatedTimeout) {
|
||||
clearTimeout(this.activatedTimeout);
|
||||
}
|
||||
this.activated = false;
|
||||
this.$nextTick(() => {
|
||||
this.activated = true;
|
||||
this.activatedTimeout = setTimeout(() => (this.activated = false), 500);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.feedback {
|
||||
position: relative;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.feedback::after {
|
||||
position: absolute;
|
||||
left: calc(100% + 5px);
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
content: '✔';
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
box-shadow: inset 0 0 0 35px rgba(111,148,182,0);
|
||||
text-shadow: none;
|
||||
position: absolute;
|
||||
left: calc(100% + 5px);
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
content: "✔";
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
box-shadow: inset 0 0 0 35px rgba(111, 148, 182, 0);
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.feedback.left::after {
|
||||
left: unset;
|
||||
right: calc(100% + 5px);
|
||||
left: unset;
|
||||
right: calc(100% + 5px);
|
||||
}
|
||||
|
||||
.feedback.activated::after {
|
||||
animation: feedback .5s ease-out forwards;
|
||||
animation: feedback 0.5s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes feedback {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scale3d(0.4, 0.4, 1), translateY(-50%);
|
||||
}
|
||||
80% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: scale3d(1.2, 1.2, 1), translateY(-50%);
|
||||
}
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scale3d(0.4, 0.4, 1), translateY(-50%);
|
||||
}
|
||||
80% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: scale3d(1.2, 1.2, 1), translateY(-50%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,32 +1,42 @@
|
|||
<template>
|
||||
<div class="field">
|
||||
<span class="field-title" v-if="title">{{ title }}</span>
|
||||
<vue-select :options="options" :model-value="value" @update:modelValue="setSelected" label-by="label" :value-by="getValue" :placeholder="placeholder" :close-on-select="closeOnSelect" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<span class="field-title" v-if="title">{{ title }}</span>
|
||||
<vue-select
|
||||
:options="options"
|
||||
:model-value="value"
|
||||
@update:modelValue="setSelected"
|
||||
label-by="label"
|
||||
:value-by="getValue"
|
||||
:placeholder="placeholder"
|
||||
:close-on-select="closeOnSelect"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Select',
|
||||
props: {
|
||||
title: String,
|
||||
options: Array, // https://vue-select.org/guide/options.html#options-prop
|
||||
value: [ String, Object ],
|
||||
default: [ String, Object ],
|
||||
placeholder: String,
|
||||
closeOnSelect: Boolean
|
||||
},
|
||||
emits: [ 'change' ],
|
||||
methods: {
|
||||
setSelected(value) {
|
||||
value = value || this.default;
|
||||
this.$emit('change', value);
|
||||
},
|
||||
getValue(item) {
|
||||
return item?.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "Select",
|
||||
props: {
|
||||
title: String,
|
||||
options: Array, // https://vue-select.org/guide/options.html#options-prop
|
||||
value: [String, Object],
|
||||
default: [String, Object],
|
||||
placeholder: String,
|
||||
closeOnSelect: Boolean
|
||||
},
|
||||
emits: ["change"],
|
||||
methods: {
|
||||
setSelected(value: any) {
|
||||
value = value || this.default;
|
||||
this.$emit("change", value);
|
||||
},
|
||||
getValue(item?: { value: any }) {
|
||||
return item?.value;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -35,28 +45,28 @@ export default {
|
|||
}
|
||||
|
||||
.field-buttons .vue-select {
|
||||
width: unset;
|
||||
width: unset;
|
||||
}
|
||||
|
||||
.vue-select,
|
||||
.vue-dropdown {
|
||||
border-color: rgba(var(--color), .26);
|
||||
border-color: rgba(var(--color), 0.26);
|
||||
}
|
||||
|
||||
.vue-dropdown {
|
||||
background: var(--secondary-background);
|
||||
background: var(--secondary-background);
|
||||
}
|
||||
|
||||
.vue-dropdown-item {
|
||||
color: var(--color);
|
||||
color: var(--color);
|
||||
}
|
||||
|
||||
.vue-dropdown-item.highlighted {
|
||||
background-color: var(--background-tooltip);
|
||||
background-color: var(--background-tooltip);
|
||||
}
|
||||
|
||||
.vue-dropdown-item.selected,
|
||||
.vue-dropdown-item.highlighted.selected {
|
||||
background-color: var(--bought);
|
||||
background-color: var(--bought);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,27 +1,35 @@
|
|||
<template>
|
||||
<div class="field">
|
||||
<span class="field-title" v-if="title">{{ title }}</span>
|
||||
<tooltip :display="`${value}`" :class="{ fullWidth: !title }">
|
||||
<input type="range" :value="value" @input="e => $emit('change', parseInt(e.target.value))" :min="min" :max="max" />
|
||||
</tooltip>
|
||||
</div>
|
||||
<div class="field">
|
||||
<span class="field-title" v-if="title">{{ title }}</span>
|
||||
<tooltip :display="`${value}`" :class="{ fullWidth: !title }">
|
||||
<input
|
||||
type="range"
|
||||
:value="value"
|
||||
@input="e => $emit('change', parseInt(e.target.value))"
|
||||
:min="min"
|
||||
:max="max"
|
||||
/>
|
||||
</tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Slider',
|
||||
props: {
|
||||
title: String,
|
||||
value: Number,
|
||||
min: Number,
|
||||
max: Number
|
||||
},
|
||||
emits: [ 'change' ]
|
||||
};
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "Slider",
|
||||
props: {
|
||||
title: String,
|
||||
value: Number,
|
||||
min: Number,
|
||||
max: Number
|
||||
},
|
||||
emits: ["change"]
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fullWidth {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,47 +1,62 @@
|
|||
<template>
|
||||
<form @submit.prevent="$emit('submit')">
|
||||
<div class="field">
|
||||
<span class="field-title" v-if="title">{{ title }}</span>
|
||||
<textarea-autosize v-if="textarea" :placeholder="placeholder" :value="value" :maxHeight="maxHeight"
|
||||
@input="value => $emit('change', value)" ref="field" />
|
||||
<input v-else type="text" :value="value" @input="e => $emit('change', e.target.value)" :placeholder="placeholder" ref="field"
|
||||
:class="{ fullWidth: !title }" />
|
||||
</div>
|
||||
</form>
|
||||
<form @submit.prevent="$emit('submit')">
|
||||
<div class="field">
|
||||
<span class="field-title" v-if="title">{{ title }}</span>
|
||||
<textarea-autosize
|
||||
v-if="textarea"
|
||||
:placeholder="placeholder"
|
||||
:value="value"
|
||||
:maxHeight="maxHeight"
|
||||
@input="value => $emit('change', value)"
|
||||
ref="field"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
type="text"
|
||||
:value="value"
|
||||
@input="e => $emit('change', e.target.value)"
|
||||
:placeholder="placeholder"
|
||||
ref="field"
|
||||
:class="{ fullWidth: !title }"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TextField',
|
||||
props: {
|
||||
title: String,
|
||||
value: String,
|
||||
textarea: Boolean,
|
||||
placeholder: String,
|
||||
maxHeight: Number
|
||||
},
|
||||
emits: [ 'change', 'submit', 'input' ],
|
||||
mounted() {
|
||||
this.$refs.field.focus();
|
||||
}
|
||||
};
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "TextField",
|
||||
props: {
|
||||
title: String,
|
||||
value: String,
|
||||
textarea: Boolean,
|
||||
placeholder: String,
|
||||
maxHeight: Number
|
||||
},
|
||||
emits: ["change", "submit"],
|
||||
mounted() {
|
||||
(this.$refs.field as HTMLElement).focus();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
form {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.field > * {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 50%;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.fullWidth {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,98 +1,101 @@
|
|||
<template>
|
||||
<label class="field">
|
||||
<input type="checkbox" class="toggle" :checked="value" @input="handleInput" />
|
||||
<span>{{ title }}</span>
|
||||
</label>
|
||||
<label class="field">
|
||||
<input type="checkbox" class="toggle" :checked="value" @input="handleInput" />
|
||||
<span>{{ title }}</span>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
// Reference: https://codepen.io/finnhvman/pen/pOeyjE
|
||||
export default {
|
||||
name: 'Toggle',
|
||||
props: {
|
||||
title: String,
|
||||
value: Boolean
|
||||
},
|
||||
emits: [ 'change' ],
|
||||
methods: {
|
||||
handleInput(e) {
|
||||
this.$emit('change', e.target.checked);
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "Toggle",
|
||||
props: {
|
||||
title: String,
|
||||
value: Boolean
|
||||
},
|
||||
emits: ["change"],
|
||||
methods: {
|
||||
handleInput(e: InputEvent) {
|
||||
this.$emit("change", (e.target as HTMLInputElement).checked);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.field {
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input {
|
||||
appearance: none;
|
||||
pointer-events: none;
|
||||
appearance: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
span {
|
||||
width: 100%;
|
||||
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;
|
||||
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;
|
||||
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);
|
||||
background-color: rgba(33, 150, 243, 0.6);
|
||||
}
|
||||
|
||||
input:checked + span::after {
|
||||
background-color: rgb(33, 150, 243);
|
||||
transform: translateX(16px);
|
||||
background-color: rgb(33, 150, 243);
|
||||
transform: translateX(16px);
|
||||
}
|
||||
|
||||
/* active */
|
||||
input:active + span::before {
|
||||
background-color: rgba(33, 150, 243, 0.6);
|
||||
background-color: rgba(33, 150, 243, 0.6);
|
||||
}
|
||||
|
||||
input:checked:active + span::before {
|
||||
background-color: rgba(0, 0, 0, 0.38);
|
||||
background-color: rgba(0, 0, 0, 0.38);
|
||||
}
|
||||
|
||||
/* disabled */
|
||||
input:disabled + span {
|
||||
color: black;
|
||||
opacity: 0.38;
|
||||
cursor: default;
|
||||
color: black;
|
||||
opacity: 0.38;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
input:disabled + span::before {
|
||||
background-color: rgba(0, 0, 0, 0.38);
|
||||
background-color: rgba(0, 0, 0, 0.38);
|
||||
}
|
||||
|
||||
input:checked:disabled + span::before {
|
||||
background-color: rgba(33, 150, 243, 0.6);
|
||||
background-color: rgba(33, 150, 243, 0.6);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
// Import and register all components,
|
||||
// which will allow us to use them in any template strings anywhere in the project
|
||||
|
||||
//import TransitionExpand from 'vue-transition-expand';
|
||||
//import 'vue-transition-expand/dist/vue-transition-expand.css';
|
||||
import CollapseTransition from '@ivanv/vue-collapse-transition/src/CollapseTransition.vue';
|
||||
import VueTextareaAutosize from 'vue-textarea-autosize';
|
||||
import Sortable from 'vue-sortable';
|
||||
import VueNextSelect from 'vue-next-select';
|
||||
import 'vue-next-select/dist/index.css';
|
||||
|
||||
export function registerComponents(vue) {
|
||||
/* from files */
|
||||
const componentsContext = require.context('./');
|
||||
componentsContext.keys().forEach(path => {
|
||||
const component = componentsContext(path).default;
|
||||
if (component && !(component.name in vue._context.components)) {
|
||||
vue.component(component.name, component);
|
||||
}
|
||||
});
|
||||
|
||||
/* from packages */
|
||||
//vue.use(TransitionExpand);
|
||||
vue.component('collapse-transition', CollapseTransition);
|
||||
vue.use(VueTextareaAutosize);
|
||||
vue.use(Sortable);
|
||||
vue.component('vue-select', VueNextSelect);
|
||||
}
|
26
src/components/index.ts
Normal file
26
src/components/index.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Import and register all components,
|
||||
// which will allow us to use them in any template strings anywhere in the project
|
||||
|
||||
import CollapseTransition from "@ivanv/vue-collapse-transition/src/CollapseTransition.vue";
|
||||
import VueTextareaAutosize from "vue-textarea-autosize";
|
||||
import Sortable from "vue-sortable";
|
||||
import VueNextSelect from "vue-next-select";
|
||||
import "vue-next-select/dist/index.css";
|
||||
import { App } from "vue";
|
||||
|
||||
export function registerComponents(vue: App): void {
|
||||
/* from files */
|
||||
const componentsContext = require.context("./");
|
||||
componentsContext.keys().forEach(path => {
|
||||
const component = componentsContext(path).default;
|
||||
if (component && !(component.name in vue._context.components)) {
|
||||
vue.component(component.name, component);
|
||||
}
|
||||
});
|
||||
|
||||
/* from packages */
|
||||
vue.component("collapse-transition", CollapseTransition);
|
||||
vue.use(VueTextareaAutosize);
|
||||
vue.use(Sortable);
|
||||
vue.component("vue-select", VueNextSelect);
|
||||
}
|
|
@ -1,13 +1,15 @@
|
|||
<template>
|
||||
<div class="table">
|
||||
<div class="col">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<div class="table">
|
||||
<div class="col">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'column'
|
||||
};
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "column"
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,43 +1,42 @@
|
|||
<template>
|
||||
<infobox v-if="infobox != undefined" :id="infobox" />
|
||||
<main-display />
|
||||
<sticky v-if="showPrestigeButton"><prestige-button /></sticky>
|
||||
<resource-display />
|
||||
<milestones />
|
||||
<component v-if="midsection" :is="midsection" />
|
||||
<clickables />
|
||||
<buyables />
|
||||
<upgrades />
|
||||
<challenges />
|
||||
<achievements />
|
||||
<infobox v-if="infobox != undefined" :id="infobox" />
|
||||
<main-display />
|
||||
<sticky v-if="showPrestigeButton"><prestige-button /></sticky>
|
||||
<resource-display />
|
||||
<milestones />
|
||||
<component v-if="midsection" :is="midsection" />
|
||||
<clickables />
|
||||
<buyables />
|
||||
<upgrades />
|
||||
<challenges />
|
||||
<achievements />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'default-layer-tab',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String
|
||||
},
|
||||
computed: {
|
||||
infobox() {
|
||||
return layers[this.layer || this.tab.layer].infoboxes && Object.keys(layers[this.layer || this.tab.layer].infoboxes)[0];
|
||||
},
|
||||
midsection() {
|
||||
if (layers[this.layer || this.tab.layer].midsection) {
|
||||
return coerceComponent(layers[this.layer || this.tab.layer].midsection);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
showPrestigeButton() {
|
||||
return layers[this.layer || this.tab.layer].type !== "none";
|
||||
},
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "default-layer-tab",
|
||||
mixins: [InjectLayerMixin],
|
||||
computed: {
|
||||
infobox(): string | undefined {
|
||||
return (
|
||||
layers[this.layer].infoboxes && Object.keys(layers[this.layer].infoboxes!.data)[0]
|
||||
);
|
||||
},
|
||||
midsection(): Component | string | null {
|
||||
if (layers[this.layer].midsection) {
|
||||
return coerceComponent(layers[this.layer].midsection!);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
showPrestigeButton(): boolean {
|
||||
return layers[this.layer].type !== "none";
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -1,66 +1,77 @@
|
|||
<template>
|
||||
<Modal :show="show">
|
||||
<template v-slot:header>
|
||||
<div class="game-over-modal-header">
|
||||
<img class="game-over-modal-logo" v-if="logo" :src="logo" :alt="modInfo" />
|
||||
<div class="game-over-modal-title">
|
||||
<h2>Congratulations!</h2>
|
||||
<h4>You've beaten {{ title }} v{{ versionNumber }}: {{ versionTitle }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:body="{ shown }">
|
||||
<div v-if="shown">
|
||||
<div>It took you {{ timePlayed }} to beat the game.</div>
|
||||
<br>
|
||||
<div>Please check the Discord to discuss the game or to check for new content updates!</div>
|
||||
<br>
|
||||
<div>
|
||||
<a :href="discordLink">
|
||||
<img src="images/discord.png" class="game-over-modal-discord" />
|
||||
{{ discordName }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<div class="game-over-footer">
|
||||
<button @click="keepGoing" class="button">Keep Going</button>
|
||||
<button @click="playAgain" class="button danger">Play Again</button>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
<Modal :show="show">
|
||||
<template v-slot:header>
|
||||
<div class="game-over-modal-header">
|
||||
<img class="game-over-modal-logo" v-if="logo" :src="logo" :alt="modInfo" />
|
||||
<div class="game-over-modal-title">
|
||||
<h2>Congratulations!</h2>
|
||||
<h4>You've beaten {{ title }} v{{ versionNumber }}: {{ versionTitle }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:body="{ shown }">
|
||||
<div v-if="shown">
|
||||
<div>It took you {{ timePlayed }} to beat the game.</div>
|
||||
<br />
|
||||
<div>
|
||||
Please check the Discord to discuss the game or to check for new content
|
||||
updates!
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<a :href="discordLink">
|
||||
<img src="images/discord.png" class="game-over-modal-discord" />
|
||||
{{ discordName }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<div class="game-over-footer">
|
||||
<button @click="keepGoing" class="button">Keep Going</button>
|
||||
<button @click="playAgain" class="button danger">Play Again</button>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modInfo from '../../data/modInfo.json';
|
||||
import { hasWon } from '../../data/mod';
|
||||
import { formatTime } from '../../util/bignum';
|
||||
import player from '../../game/player';
|
||||
<script lang="ts">
|
||||
import { hasWon } from "@/data/mod";
|
||||
import modInfo from "@/data/modInfo.json";
|
||||
import player from "@/game/player";
|
||||
import { formatTime } from "@/util/bignum";
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'GameOverScreen',
|
||||
data() {
|
||||
const { title, logo, discordName, discordLink, versionNumber, versionTitle } = modInfo;
|
||||
return { title, logo, discordName, discordLink, versionNumber, versionTitle };
|
||||
},
|
||||
computed: {
|
||||
timePlayed() {
|
||||
return formatTime(player.timePlayed);
|
||||
},
|
||||
show() {
|
||||
return hasWon.value && !player.keepGoing;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
keepGoing() {
|
||||
player.keepGoing = true;
|
||||
},
|
||||
playAgain() {
|
||||
console.warn("Not yet implemented!");
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "GameOverScreen",
|
||||
data() {
|
||||
const { title, logo, discordName, discordLink, versionNumber, versionTitle } = modInfo;
|
||||
return {
|
||||
title,
|
||||
logo,
|
||||
discordName,
|
||||
discordLink,
|
||||
versionNumber,
|
||||
versionTitle
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
timePlayed() {
|
||||
return formatTime(player.timePlayed);
|
||||
},
|
||||
show() {
|
||||
return hasWon.value && !player.keepGoing;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
keepGoing() {
|
||||
player.keepGoing = true;
|
||||
},
|
||||
playAgain() {
|
||||
console.warn("Not yet implemented!");
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -71,36 +82,36 @@ export default {
|
|||
}
|
||||
|
||||
.game-over-modal-header * {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.game-over-modal-logo {
|
||||
height: 4em;
|
||||
height: 4em;
|
||||
width: 4em;
|
||||
}
|
||||
|
||||
.game-over-modal-title {
|
||||
padding: 10px 0;
|
||||
padding: 10px 0;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.game-over-footer {
|
||||
display: flex;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.game-over-footer button {
|
||||
margin: 0 10px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.game-over-modal-discord-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.game-over-modal-discord {
|
||||
height: 2em;
|
||||
margin: 0;
|
||||
margin-right: 4px;
|
||||
height: 2em;
|
||||
margin: 0;
|
||||
margin-right: 4px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,98 +1,115 @@
|
|||
<template>
|
||||
<Modal :show="show" @close="$emit('closeDialog', 'Info')">
|
||||
<template v-slot:header>
|
||||
<div 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>
|
||||
</template>
|
||||
<template v-slot:body="{ shown }">
|
||||
<div v-if="shown">
|
||||
<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'" class="info-modal-discord-link">
|
||||
<img src="images/discord.png" class="info-modal-discord" />
|
||||
{{ discordName }}
|
||||
</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">
|
||||
<br>
|
||||
<h4>Hotkeys</h4>
|
||||
<div v-for="key in hotkeys" :key="key.key">
|
||||
{{ key.key }}: {{ key.description }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
<Modal :show="show" @close="$emit('closeDialog', 'Info')">
|
||||
<template v-slot:header>
|
||||
<div 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>
|
||||
</template>
|
||||
<template v-slot:body="{ shown }">
|
||||
<div v-if="shown">
|
||||
<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'"
|
||||
class="info-modal-discord-link"
|
||||
>
|
||||
<img src="images/discord.png" class="info-modal-discord" />
|
||||
{{ discordName }}
|
||||
</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">
|
||||
<br />
|
||||
<h4>Hotkeys</h4>
|
||||
<div v-for="key in hotkeys" :key="key.key">
|
||||
{{ key.key }}: {{ key.description }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modInfo from '../../data/modInfo.json';
|
||||
import { formatTime } from '../../util/bignum';
|
||||
import { hotkeys } from '../../game/layers';
|
||||
import player from '../../game/player';
|
||||
<script lang="ts">
|
||||
import modInfo from "@/data/modInfo.json";
|
||||
import { hotkeys } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { formatTime } from "@/util/bignum";
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
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
|
||||
},
|
||||
emits: [ 'closeDialog', 'openDialog' ],
|
||||
computed: {
|
||||
timePlayed() {
|
||||
return formatTime(player.timePlayed);
|
||||
},
|
||||
hotkeys() {
|
||||
return Object.keys(hotkeys).reduce((acc, curr) => {
|
||||
if (hotkeys[curr].unlocked !== false) {
|
||||
acc[curr] = hotkeys[curr];
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "Info",
|
||||
data() {
|
||||
const {
|
||||
title,
|
||||
logo,
|
||||
author,
|
||||
discordName,
|
||||
discordLink,
|
||||
versionNumber,
|
||||
versionTitle
|
||||
} = modInfo;
|
||||
return {
|
||||
title,
|
||||
logo,
|
||||
author,
|
||||
discordName,
|
||||
discordLink,
|
||||
versionNumber,
|
||||
versionTitle
|
||||
};
|
||||
},
|
||||
props: {
|
||||
show: Boolean
|
||||
},
|
||||
emits: ["closeDialog", "openDialog"],
|
||||
computed: {
|
||||
timePlayed() {
|
||||
return formatTime(player.timePlayed);
|
||||
},
|
||||
hotkeys() {
|
||||
return hotkeys.filter(hotkey => hotkey.unlocked);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.info-modal-header {
|
||||
display: flex;
|
||||
display: flex;
|
||||
margin: -20px;
|
||||
margin-bottom: 0;
|
||||
background: var(--secondary-background);
|
||||
|
@ -100,29 +117,29 @@ export default {
|
|||
}
|
||||
|
||||
.info-modal-header * {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.info-modal-logo {
|
||||
height: 4em;
|
||||
height: 4em;
|
||||
width: 4em;
|
||||
}
|
||||
|
||||
.info-modal-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px 0;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.info-modal-discord-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.info-modal-discord {
|
||||
height: 2em;
|
||||
margin: 0;
|
||||
margin-right: 4px;
|
||||
height: 2em;
|
||||
margin: 0;
|
||||
margin-right: 4px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
<template>
|
||||
<slot />
|
||||
<slot />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'LayerProvider',
|
||||
provide() {
|
||||
return {
|
||||
'tab': {
|
||||
layer: this.layer,
|
||||
index: this.index
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
layer: String,
|
||||
index: Number
|
||||
}
|
||||
};
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "LayerProvider",
|
||||
provide() {
|
||||
return {
|
||||
tab: {
|
||||
layer: this.layer,
|
||||
index: this.index
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
layer: String,
|
||||
index: Number
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -1,175 +1,206 @@
|
|||
<template>
|
||||
<LayerProvider :layer="layer" :index="index">
|
||||
<div class="layer-container">
|
||||
<button v-if="index > 0 && allowGoBack && !minimized" class="goBack" @click="goBack(index)">←</button>
|
||||
<button class="layer-tab minimized" v-if="minimized" @click="toggleMinimized"><div>{{ name }}</div></button>
|
||||
<div class="layer-tab" :style="style" :class="{ hasSubtabs: subtabs }" v-else>
|
||||
<branches>
|
||||
<sticky v-if="subtabs" class="subtabs-container" :class="{ floating, firstTab: firstTab || !allowGoBack, minimizable }">
|
||||
<div class="subtabs">
|
||||
<tab-button v-for="(subtab, id) in subtabs" @selectTab="selectSubtab(id)" :key="id"
|
||||
:activeTab="id === activeSubtab" :options="subtab" :text="id" />
|
||||
</div>
|
||||
</sticky>
|
||||
<component v-if="display" :is="display" />
|
||||
<default-layer-tab v-else />
|
||||
</branches>
|
||||
</div>
|
||||
<button v-if="minimizable" class="minimize" @click="toggleMinimized">▼</button>
|
||||
</div>
|
||||
</LayerProvider>
|
||||
<LayerProvider :layer="layer" :index="index">
|
||||
<div class="layer-container">
|
||||
<button
|
||||
v-if="index > 0 && allowGoBack && !minimized"
|
||||
class="goBack"
|
||||
@click="goBack(index)"
|
||||
>
|
||||
←
|
||||
</button>
|
||||
<button class="layer-tab minimized" v-if="minimized" @click="toggleMinimized">
|
||||
<div>{{ name }}</div>
|
||||
</button>
|
||||
<div class="layer-tab" :style="style" :class="{ hasSubtabs: subtabs }" v-else>
|
||||
<branches>
|
||||
<sticky
|
||||
v-if="subtabs"
|
||||
class="subtabs-container"
|
||||
:class="{
|
||||
floating,
|
||||
firstTab: firstTab || !allowGoBack,
|
||||
minimizable
|
||||
}"
|
||||
>
|
||||
<div class="subtabs">
|
||||
<tab-button
|
||||
v-for="(subtab, id) in subtabs"
|
||||
@selectTab="selectSubtab(id)"
|
||||
:key="id"
|
||||
:activeTab="id === activeSubtab"
|
||||
:options="subtab"
|
||||
:text="id"
|
||||
/>
|
||||
</div>
|
||||
</sticky>
|
||||
<component v-if="display" :is="display" />
|
||||
<default-layer-tab v-else />
|
||||
</branches>
|
||||
</div>
|
||||
<button v-if="minimizable" class="minimize" @click="toggleMinimized">
|
||||
▼
|
||||
</button>
|
||||
</div>
|
||||
</LayerProvider>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import player from '../../game/player';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
import { isPlainObject } from '../../util/common';
|
||||
import modInfo from '../../data/modInfo.json';
|
||||
import themes from '../../data/themes';
|
||||
<script lang="ts">
|
||||
import modInfo from "@/data/modInfo.json";
|
||||
import themes from "@/data/themes";
|
||||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { Subtab } from "@/typings/features/subtab";
|
||||
import { coerceComponent } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'layer-tab',
|
||||
props: {
|
||||
layer: String,
|
||||
index: Number,
|
||||
forceFirstTab: Boolean,
|
||||
minimizable: Boolean,
|
||||
tab: Function
|
||||
},
|
||||
data() {
|
||||
return { allowGoBack: modInfo.allowGoBack };
|
||||
},
|
||||
computed: {
|
||||
minimized() {
|
||||
return this.minimizable && player.minimized[this.layer];
|
||||
},
|
||||
name() {
|
||||
return layers[this.layer].name;
|
||||
},
|
||||
floating() {
|
||||
return themes[player.theme].floatingTabs;
|
||||
},
|
||||
style() {
|
||||
const style = [];
|
||||
if (layers[this.layer].style) {
|
||||
style.push(layers[this.layer].style);
|
||||
}
|
||||
if (layers[this.layer].activeSubtab?.style) {
|
||||
style.push(layers[this.layer].activeSubtab.style);
|
||||
}
|
||||
return style;
|
||||
},
|
||||
display() {
|
||||
if (layers[this.layer].activeSubtab?.display) {
|
||||
return coerceComponent(layers[this.layer].activeSubtab.display);
|
||||
}
|
||||
if (layers[this.layer].display) {
|
||||
return coerceComponent(layers[this.layer].display);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
subtabs() {
|
||||
if (layers[this.layer].subtabs) {
|
||||
return Object.entries(layers[this.layer].subtabs)
|
||||
.filter(subtab => isPlainObject(subtab[1]) && subtab[1].unlocked !== false)
|
||||
.reduce((acc, curr) => {
|
||||
acc[curr[0]] = curr[1];
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
return null;
|
||||
},
|
||||
activeSubtab() {
|
||||
return layers[this.layer].activeSubtab?.id;
|
||||
},
|
||||
firstTab() {
|
||||
if (this.forceFirstTab != undefined) {
|
||||
return this.forceFirstTab;
|
||||
}
|
||||
return this.index === 0;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
minimized(newValue) {
|
||||
if (this.tab == undefined) {
|
||||
return;
|
||||
}
|
||||
const tab = this.tab();
|
||||
if (tab != undefined) {
|
||||
if (newValue) {
|
||||
tab.style.flexGrow = 0;
|
||||
tab.style.flexShrink = 0;
|
||||
tab.style.width = "60px";
|
||||
tab.style.minWidth = tab.style.flexBasis = null;
|
||||
tab.style.margin = 0;
|
||||
} else {
|
||||
tab.style.flexGrow = null;
|
||||
tab.style.flexShrink = null;
|
||||
tab.style.width = null;
|
||||
tab.style.minWidth = tab.style.flexBasis = `${layers[this.layer].minWidth}px`;
|
||||
tab.style.margin = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.tab == undefined) {
|
||||
return;
|
||||
}
|
||||
const tab = this.tab();
|
||||
if (tab != undefined) {
|
||||
if (this.minimized) {
|
||||
tab.style.flexGrow = 0;
|
||||
tab.style.flexShrink = 0;
|
||||
tab.style.width = "60px";
|
||||
tab.style.minWidth = tab.style.flexBasis = null;
|
||||
tab.style.margin = 0;
|
||||
} else {
|
||||
tab.style.flexGrow = null;
|
||||
tab.style.flexShrink = null;
|
||||
tab.style.width = null;
|
||||
tab.style.minWidth = tab.style.flexBasis = `${layers[this.layer].minWidth}px`;
|
||||
tab.style.margin = null;
|
||||
}
|
||||
} else {
|
||||
this.$nextTick(this.mounted);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectSubtab(subtab) {
|
||||
player.subtabs[this.layer].mainTabs = subtab;
|
||||
},
|
||||
toggleMinimized() {
|
||||
player.minimized[this.layer] = !player.minimized[this.layer];
|
||||
},
|
||||
goBack(index) {
|
||||
player.tabs = player.tabs.slice(0, index);
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "layer-tab",
|
||||
props: {
|
||||
layer: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
index: Number,
|
||||
forceFirstTab: Boolean,
|
||||
minimizable: Boolean,
|
||||
tab: Function
|
||||
},
|
||||
data() {
|
||||
return { allowGoBack: modInfo.allowGoBack };
|
||||
},
|
||||
computed: {
|
||||
minimized(): boolean {
|
||||
return this.minimizable && player.minimized[this.layer];
|
||||
},
|
||||
name(): string {
|
||||
return layers[this.layer].name || this.layer;
|
||||
},
|
||||
floating(): boolean {
|
||||
return themes[player.theme].floatingTabs;
|
||||
},
|
||||
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
const style = [];
|
||||
if (layers[this.layer].style) {
|
||||
style.push(layers[this.layer].style);
|
||||
}
|
||||
if (layers[this.layer].activeSubtab?.style) {
|
||||
style.push(layers[this.layer].activeSubtab!.style);
|
||||
}
|
||||
return style;
|
||||
},
|
||||
display(): Component | string | null {
|
||||
if (layers[this.layer].activeSubtab?.display) {
|
||||
return coerceComponent(layers[this.layer].activeSubtab!.display!);
|
||||
}
|
||||
if (layers[this.layer].display) {
|
||||
return coerceComponent(layers[this.layer].display!);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
subtabs(): Record<string, Subtab> | null {
|
||||
if (layers[this.layer].subtabs) {
|
||||
return Object.entries(layers[this.layer].subtabs!)
|
||||
.filter(subtab => subtab[1].unlocked !== false)
|
||||
.reduce((acc: Record<string, Subtab>, curr: [string, Subtab]) => {
|
||||
acc[curr[0]] = curr[1];
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
return null;
|
||||
},
|
||||
activeSubtab(): string | undefined {
|
||||
return layers[this.layer].activeSubtab?.id;
|
||||
},
|
||||
firstTab(): boolean {
|
||||
if (this.forceFirstTab != undefined) {
|
||||
return this.forceFirstTab;
|
||||
}
|
||||
return this.index === 0;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
minimized(newValue) {
|
||||
if (this.tab == undefined) {
|
||||
return;
|
||||
}
|
||||
const tab = this.tab();
|
||||
if (tab != undefined) {
|
||||
if (newValue) {
|
||||
tab.style.flexGrow = 0;
|
||||
tab.style.flexShrink = 0;
|
||||
tab.style.width = "60px";
|
||||
tab.style.minWidth = tab.style.flexBasis = null;
|
||||
tab.style.margin = 0;
|
||||
} else {
|
||||
tab.style.flexGrow = null;
|
||||
tab.style.flexShrink = null;
|
||||
tab.style.width = null;
|
||||
tab.style.minWidth = tab.style.flexBasis = `${layers[this.layer].minWidth}px`;
|
||||
tab.style.margin = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.setup();
|
||||
},
|
||||
methods: {
|
||||
setup() {
|
||||
if (this.tab == undefined) {
|
||||
return;
|
||||
}
|
||||
const tab = this.tab();
|
||||
if (tab != undefined) {
|
||||
if (this.minimized) {
|
||||
tab.style.flexGrow = 0;
|
||||
tab.style.flexShrink = 0;
|
||||
tab.style.width = "60px";
|
||||
tab.style.minWidth = tab.style.flexBasis = null;
|
||||
tab.style.margin = 0;
|
||||
} else {
|
||||
tab.style.flexGrow = null;
|
||||
tab.style.flexShrink = null;
|
||||
tab.style.width = null;
|
||||
tab.style.minWidth = tab.style.flexBasis = `${layers[this.layer].minWidth}px`;
|
||||
tab.style.margin = null;
|
||||
}
|
||||
} else {
|
||||
this.$nextTick(this.setup);
|
||||
}
|
||||
},
|
||||
selectSubtab(subtab: string) {
|
||||
player.subtabs[this.layer].mainTabs = subtab;
|
||||
},
|
||||
toggleMinimized() {
|
||||
player.minimized[this.layer] = !player.minimized[this.layer];
|
||||
},
|
||||
goBack(index: number) {
|
||||
player.tabs = player.tabs.slice(0, index);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.layer-container {
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.layer-tab:not(.minimized) {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
min-height: 100%;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
min-height: 100%;
|
||||
flex-grow: 1;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.inner-tab > .layer-container > .layer-tab:not(.minimized) {
|
||||
padding-top: 50px;
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.layer-tab.minimized {
|
||||
|
@ -191,18 +222,18 @@ export default {
|
|||
|
||||
.layer-tab.minimized div {
|
||||
margin: 0;
|
||||
writing-mode: vertical-rl;
|
||||
writing-mode: vertical-rl;
|
||||
padding-left: 10px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.inner-tab > .layer-container > .layer-tab:not(.minimized) {
|
||||
margin: -50px -10px;
|
||||
padding: 50px 10px;
|
||||
margin: -50px -10px;
|
||||
padding: 50px 10px;
|
||||
}
|
||||
|
||||
.layer-tab .subtabs {
|
||||
margin-bottom: 24px;
|
||||
margin-bottom: 24px;
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
padding-right: 60px;
|
||||
|
@ -210,38 +241,38 @@ export default {
|
|||
}
|
||||
|
||||
.subtabs-container:not(.floating) {
|
||||
border-top: solid 4px var(--separator);
|
||||
border-bottom: solid 4px var(--separator);
|
||||
border-top: solid 4px var(--separator);
|
||||
border-bottom: solid 4px var(--separator);
|
||||
}
|
||||
|
||||
.subtabs-container:not(.floating) .subtabs {
|
||||
width: calc(100% + 14px);
|
||||
margin-left: -7px;
|
||||
margin-right: -7px;
|
||||
box-sizing: border-box;
|
||||
text-align: left;
|
||||
padding-left: 14px;
|
||||
margin-bottom: -4px;
|
||||
width: calc(100% + 14px);
|
||||
margin-left: -7px;
|
||||
margin-right: -7px;
|
||||
box-sizing: border-box;
|
||||
text-align: left;
|
||||
padding-left: 14px;
|
||||
margin-bottom: -4px;
|
||||
}
|
||||
|
||||
.subtabs-container.floating .subtabs {
|
||||
justify-content: center;
|
||||
margin-top: -25px;
|
||||
justify-content: center;
|
||||
margin-top: -25px;
|
||||
}
|
||||
|
||||
.modal-body .layer-tab {
|
||||
padding-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.modal-body .layer-tab:not(.hasSubtabs) {
|
||||
padding-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.modal-body .subtabs {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding-left: 0;
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.subtabs-container:not(.floating).firstTab .subtabs {
|
||||
|
@ -250,7 +281,7 @@ export default {
|
|||
}
|
||||
|
||||
.subtabs-container:not(.floating):first-child {
|
||||
border-top: 0;
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.subtabs-container.minimizable:not(.floating):first-child {
|
||||
|
@ -258,11 +289,11 @@ export default {
|
|||
}
|
||||
|
||||
.subtabs-container:not(.floating):first-child .subtabs {
|
||||
margin-top: -50px;
|
||||
margin-top: -50px;
|
||||
}
|
||||
|
||||
.subtabs-container:not(.floating):not(.firstTab) .subtabs {
|
||||
padding-left: 70px;
|
||||
padding-left: 70px;
|
||||
}
|
||||
|
||||
.minimize {
|
||||
|
@ -283,7 +314,7 @@ export default {
|
|||
}
|
||||
|
||||
.minimized + .minimize {
|
||||
transform: rotate(-90deg);
|
||||
transform: rotate(-90deg);
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,83 +1,104 @@
|
|||
<template>
|
||||
<div v-if="microtabs" class="microtabs">
|
||||
<LayerProvider :layer="layer || tab.layer" :index="tab.index">
|
||||
<div v-if="microtabs" class="tabs" :class="{ floating }">
|
||||
<tab-button v-for="(microtab, id) in microtabs" @selectTab="selectMicrotab(id)" :key="id"
|
||||
:activeTab="id === activeMicrotab.id" :options="microtab" :text="id" />
|
||||
</div>
|
||||
<layer-tab v-if="embed" :layer="embed" />
|
||||
<component v-else :is="display" />
|
||||
</LayerProvider>
|
||||
</div>
|
||||
<div v-if="microtabs" class="microtabs">
|
||||
<LayerProvider :layer="layer" :index="tab.index">
|
||||
<div v-if="microtabs" class="tabs" :class="{ floating }">
|
||||
<tab-button
|
||||
v-for="(microtab, id) in microtabs"
|
||||
@selectTab="selectMicrotab(id)"
|
||||
:key="id"
|
||||
:activeTab="id === activeMicrotab?.id"
|
||||
:options="microtab"
|
||||
:text="id"
|
||||
/>
|
||||
</div>
|
||||
<template v-if="activeMicrotab">
|
||||
<layer-tab v-if="embed" :layer="embed" />
|
||||
<component v-else :is="display" />
|
||||
</template>
|
||||
</LayerProvider>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import player from '../../game/player';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
import themes from '../../data/themes';
|
||||
<script lang="ts">
|
||||
import themes from "@/data/themes";
|
||||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { Microtab, MicrotabFamily } from "@/typings/features/subtab";
|
||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'microtab',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
family: String,
|
||||
id: String
|
||||
},
|
||||
computed: {
|
||||
floating() {
|
||||
return themes[player.theme].floatingTabs;
|
||||
},
|
||||
tabFamily() {
|
||||
return layers[this.layer || this.tab.layer].microtabs[this.family];
|
||||
},
|
||||
microtabs() {
|
||||
return Object.keys(this.tabFamily)
|
||||
.filter(microtab =>
|
||||
microtab !== 'activeMicrotab' && this.tabFamily[microtab].isProxy && this.tabFamily[microtab].unlocked !== false)
|
||||
.reduce((acc, curr) => {
|
||||
acc[curr] = this.tabFamily[curr];
|
||||
return acc;
|
||||
}, {});
|
||||
},
|
||||
activeMicrotab() {
|
||||
return this.id != undefined ? this.tabFamily[this.id] : this.tabFamily.activeMicrotab;
|
||||
},
|
||||
embed() {
|
||||
return this.activeMicrotab.embedLayer;
|
||||
},
|
||||
display() {
|
||||
return coerceComponent(this.activeMicrotab.display);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectMicrotab(tab) {
|
||||
player.subtabs[this.layer || this.tab.layer][this.family] = tab;
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "microtab",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
family: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
floating() {
|
||||
return themes[player.theme].floatingTabs;
|
||||
},
|
||||
tabFamily(): MicrotabFamily {
|
||||
return layers[this.layer].microtabs![this.family];
|
||||
},
|
||||
microtabs(): Record<string, Microtab> {
|
||||
return Object.keys(this.tabFamily.data)
|
||||
.filter(
|
||||
microtab =>
|
||||
microtab !== "activeMicrotab" &&
|
||||
this.tabFamily.data[microtab].isProxy &&
|
||||
this.tabFamily.data[microtab].unlocked !== false
|
||||
)
|
||||
.reduce((acc: Record<string, Microtab>, curr) => {
|
||||
acc[curr] = this.tabFamily.data[curr];
|
||||
return acc;
|
||||
}, {});
|
||||
},
|
||||
activeMicrotab(): Microtab | undefined {
|
||||
return this.id != undefined
|
||||
? this.tabFamily.data[this.id]
|
||||
: this.tabFamily.activeMicrotab;
|
||||
},
|
||||
embed(): string | undefined {
|
||||
return this.activeMicrotab!.embedLayer;
|
||||
},
|
||||
display(): Component | string | undefined {
|
||||
return this.activeMicrotab!.display && coerceComponent(this.activeMicrotab!.display!);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectMicrotab(tab: string) {
|
||||
player.subtabs[this.layer][this.family] = tab;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.microtabs {
|
||||
margin: var(--feature-margin) -11px;
|
||||
position: relative;
|
||||
border: solid 4px var(--separator);
|
||||
margin: var(--feature-margin) -11px;
|
||||
position: relative;
|
||||
border: solid 4px var(--separator);
|
||||
}
|
||||
|
||||
.tabs:not(.floating) {
|
||||
text-align: left;
|
||||
border-bottom: inherit;
|
||||
border-width: 4px;
|
||||
box-sizing: border-box;
|
||||
height: 50px;
|
||||
text-align: left;
|
||||
border-bottom: inherit;
|
||||
border-width: 4px;
|
||||
box-sizing: border-box;
|
||||
height: 50px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.microtabs .sticky {
|
||||
margin-left: unset !important;
|
||||
margin-right: unset !important;
|
||||
margin-left: unset !important;
|
||||
margin-right: unset !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,92 +1,106 @@
|
|||
<template>
|
||||
<teleport to="#modal-root">
|
||||
<transition name="modal" @before-enter="setAnimating(true)" @after-leave="setAnimating(false)">
|
||||
<div class="modal-mask" v-show="show" v-on:pointerdown.self="$emit('close')" v-bind="$attrs">
|
||||
<div class="modal-wrapper">
|
||||
<div class="modal-container">
|
||||
<div class="modal-header">
|
||||
<slot name="header" :shown="isVisible">
|
||||
default header
|
||||
</slot>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<branches>
|
||||
<slot name="body" :shown="isVisible">
|
||||
default body
|
||||
</slot>
|
||||
</branches>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<slot name="footer" :shown="isVisible">
|
||||
<div class="modal-default-footer">
|
||||
<div class="modal-default-flex-grow"></div>
|
||||
<button class="button modal-default-button" @click="$emit('close')">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</teleport>
|
||||
<teleport to="#modal-root">
|
||||
<transition
|
||||
name="modal"
|
||||
@before-enter="setAnimating(true)"
|
||||
@after-leave="setAnimating(false)"
|
||||
>
|
||||
<div
|
||||
class="modal-mask"
|
||||
v-show="show"
|
||||
v-on:pointerdown.self="$emit('close')"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<div class="modal-wrapper">
|
||||
<div class="modal-container">
|
||||
<div class="modal-header">
|
||||
<slot name="header" :shown="isVisible">
|
||||
default header
|
||||
</slot>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<branches>
|
||||
<slot name="body" :shown="isVisible">
|
||||
default body
|
||||
</slot>
|
||||
</branches>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<slot name="footer" :shown="isVisible">
|
||||
<div class="modal-default-footer">
|
||||
<div class="modal-default-flex-grow"></div>
|
||||
<button
|
||||
class="button modal-default-button"
|
||||
@click="$emit('close')"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Modal',
|
||||
data() {
|
||||
return {
|
||||
isAnimating: false
|
||||
}
|
||||
},
|
||||
props: {
|
||||
show: Boolean
|
||||
},
|
||||
emits: [ 'close' ],
|
||||
computed: {
|
||||
isVisible() {
|
||||
return this.show || this.isAnimating;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setAnimating(isAnimating) {
|
||||
this.isAnimating = isAnimating;
|
||||
}
|
||||
}
|
||||
}
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "Modal",
|
||||
data() {
|
||||
return {
|
||||
isAnimating: false
|
||||
};
|
||||
},
|
||||
props: {
|
||||
show: Boolean
|
||||
},
|
||||
emits: ["close"],
|
||||
computed: {
|
||||
isVisible(): boolean {
|
||||
return this.show || this.isAnimating;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setAnimating(isAnimating: boolean) {
|
||||
this.isAnimating = isAnimating;
|
||||
}
|
||||
}
|
||||
});
|
||||
</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;
|
||||
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%);
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
width: 640px;
|
||||
max-width: 95vw;
|
||||
width: 640px;
|
||||
max-width: 95vw;
|
||||
max-height: 95vh;
|
||||
background-color: var(--background);
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
transition: all 0.3s ease;
|
||||
text-align: left;
|
||||
border: var(--modal-border);
|
||||
background-color: var(--background);
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
transition: all 0.3s ease;
|
||||
text-align: left;
|
||||
border: var(--modal-border);
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -97,7 +111,7 @@ export default {
|
|||
}
|
||||
|
||||
.modal-body {
|
||||
margin: 20px 0;
|
||||
margin: 20px 0;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
@ -108,24 +122,24 @@ export default {
|
|||
}
|
||||
|
||||
.modal-default-footer {
|
||||
display: flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.modal-default-flex-grow {
|
||||
flex-grow: 1;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.modal-enter-from {
|
||||
opacity: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.modal-leave-active {
|
||||
opacity: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.modal-enter-from .modal-container,
|
||||
.modal-leave-active .modal-container {
|
||||
-webkit-transform: scale(1.1);
|
||||
transform: scale(1.1);
|
||||
-webkit-transform: scale(1.1);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,115 +1,137 @@
|
|||
<template>
|
||||
<Modal :show="hasNaN" v-bind="$attrs">
|
||||
<template v-slot:header>
|
||||
<div class="nan-modal-header">
|
||||
<h2>NaN value detected!</h2>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<div>Attempted to assign "{{ path }}" to NaN (previously {{ format(previous) }}). Auto-saving has been {{ autosave ? 'enabled' : 'disabled' }}. Check the console for more details, and consider sharing it with the developers on discord.</div>
|
||||
<br>
|
||||
<div>
|
||||
<a :href="discordLink" class="nan-modal-discord-link">
|
||||
<img src="images/discord.png" class="nan-modal-discord" />
|
||||
{{ discordName }}
|
||||
</a>
|
||||
</div>
|
||||
<br>
|
||||
<Toggle title="Autosave" :value="autosave" @change="setAutosave" />
|
||||
<Toggle title="Pause game" :value="paused" @change="togglePaused" />
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<div class="nan-footer">
|
||||
<button @click="toggleSavesManager" class="button">Open Saves Manager</button>
|
||||
<button @click="setZero" class="button">Set to 0</button>
|
||||
<button @click="setOne" class="button">Set to 1</button>
|
||||
<button @click="setPrev" class="button" v-if="previous && previous.neq(0) && previous.neq(1)">Set to previous</button>
|
||||
<button @click="ignore" class="button danger">Ignore</button>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
<SavesManager :show="showSaves" @closeDialog="toggleSavesManager" />
|
||||
<Modal :show="hasNaN" v-bind="$attrs">
|
||||
<template v-slot:header>
|
||||
<div class="nan-modal-header">
|
||||
<h2>NaN value detected!</h2>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<div>
|
||||
Attempted to assign "{{ path }}" to NaN (previously {{ format(previous) }}).
|
||||
Auto-saving has been {{ autosave ? "enabled" : "disabled" }}. Check the console for
|
||||
more details, and consider sharing it with the developers on discord.
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<a :href="discordLink" class="nan-modal-discord-link">
|
||||
<img src="images/discord.png" class="nan-modal-discord" />
|
||||
{{ discordName }}
|
||||
</a>
|
||||
</div>
|
||||
<br />
|
||||
<Toggle title="Autosave" :value="autosave" @change="setAutosave" />
|
||||
<Toggle title="Pause game" :value="paused" @change="togglePaused" />
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<div class="nan-footer">
|
||||
<button @click="toggleSavesManager" class="button">
|
||||
Open Saves Manager
|
||||
</button>
|
||||
<button @click="setZero" class="button">Set to 0</button>
|
||||
<button @click="setOne" class="button">Set to 1</button>
|
||||
<button
|
||||
@click="setPrev"
|
||||
class="button"
|
||||
v-if="previous && previous.neq(0) && previous.neq(1)"
|
||||
>
|
||||
Set to previous
|
||||
</button>
|
||||
<button @click="ignore" class="button danger">Ignore</button>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
<SavesManager :show="showSaves" @closeDialog="toggleSavesManager" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modInfo from '../../data/modInfo.json';
|
||||
import Decimal, { format } from '../../util/bignum';
|
||||
import { mapState } from '../../util/vue';
|
||||
import player from '../../game/player';
|
||||
<script lang="ts">
|
||||
import modInfo from "@/data/modInfo.json";
|
||||
import player from "@/game/player";
|
||||
import Decimal, { format } from "@/util/bignum";
|
||||
import { mapState } from "@/util/vue";
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'NaNScreen',
|
||||
data() {
|
||||
const { discordName, discordLink } = modInfo;
|
||||
return { discordName, discordLink, format, showSaves: false };
|
||||
},
|
||||
computed: {
|
||||
...mapState([ 'hasNaN', 'autosave' ]),
|
||||
path() {
|
||||
return player.NaNPath.join('.');
|
||||
},
|
||||
previous() {
|
||||
return player.NaNReceiver?.[this.property];
|
||||
},
|
||||
paused() {
|
||||
return player.devSpeed === 0;
|
||||
},
|
||||
property() {
|
||||
return player.NaNPath.slice(-1)[0];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setZero() {
|
||||
player.NaNReceiver[this.property] = new Decimal(0);
|
||||
player.hasNaN = false;
|
||||
},
|
||||
setOne() {
|
||||
player.NaNReceiver[this.property] = new Decimal(1);
|
||||
player.hasNaN = false;
|
||||
},
|
||||
setPrev() {
|
||||
player.hasNaN = false;
|
||||
},
|
||||
ignore() {
|
||||
player.NaNReceiver[this.property] = new Decimal(NaN);
|
||||
player.hasNaN = false;
|
||||
},
|
||||
setAutosave(autosave) {
|
||||
player.autosave = autosave;
|
||||
},
|
||||
toggleSavesManager() {
|
||||
this.showSaves = !this.showSaves;
|
||||
},
|
||||
togglePaused() {
|
||||
player.devSpeed = this.paused ? 1 : 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "NaNScreen",
|
||||
data() {
|
||||
const { discordName, discordLink } = modInfo;
|
||||
return { discordName, discordLink, format, showSaves: false };
|
||||
},
|
||||
computed: {
|
||||
...mapState(["hasNaN", "autosave"]),
|
||||
path(): string | undefined {
|
||||
return player.NaNPath?.join(".");
|
||||
},
|
||||
previous(): any {
|
||||
if (player.NaNReceiver && this.property) {
|
||||
return player.NaNReceiver[this.property];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
paused() {
|
||||
return player.devSpeed === 0;
|
||||
},
|
||||
property(): string | undefined {
|
||||
return player.NaNPath?.slice(-1)[0];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setZero() {
|
||||
if (player.NaNReceiver && this.property) {
|
||||
player.NaNReceiver[this.property] = new Decimal(0);
|
||||
player.hasNaN = false;
|
||||
}
|
||||
},
|
||||
setOne() {
|
||||
if (player.NaNReceiver && this.property) {
|
||||
player.NaNReceiver[this.property] = new Decimal(1);
|
||||
player.hasNaN = false;
|
||||
}
|
||||
},
|
||||
setPrev() {
|
||||
player.hasNaN = false;
|
||||
},
|
||||
ignore() {
|
||||
if (player.NaNReceiver && this.property) {
|
||||
player.NaNReceiver[this.property] = new Decimal(NaN);
|
||||
player.hasNaN = false;
|
||||
}
|
||||
},
|
||||
setAutosave(autosave: boolean) {
|
||||
player.autosave = autosave;
|
||||
},
|
||||
toggleSavesManager() {
|
||||
this.showSaves = !this.showSaves;
|
||||
},
|
||||
togglePaused() {
|
||||
player.devSpeed = this.paused ? 1 : 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.nan-modal-header {
|
||||
padding: 10px 0;
|
||||
padding: 10px 0;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.nan-footer {
|
||||
display: flex;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.nan-footer button {
|
||||
margin: 0 10px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.nan-modal-discord-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nan-modal-discord {
|
||||
height: 2em;
|
||||
margin: 0;
|
||||
margin-right: 4px;
|
||||
height: 2em;
|
||||
margin: 0;
|
||||
margin-right: 4px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,146 +1,174 @@
|
|||
<template>
|
||||
<div class="nav" v-if="useHeader" v-bind="$attrs">
|
||||
<img v-if="banner" :src="banner" height="100%" :alt="title" />
|
||||
<div v-else class="title">{{ title }}</div>
|
||||
<div @click="openDialog('Changelog')" class="version-container">
|
||||
<tooltip display="Changelog" bottom class="version"><span>v{{ version }}</span></tooltip>
|
||||
</div>
|
||||
<div style="flex-grow: 1; cursor: unset;"></div>
|
||||
<div class="discord">
|
||||
<img src="images/discord.png" @click="window.open(discordLink, 'mywindow')" />
|
||||
<ul class="discord-links">
|
||||
<li v-if="discordLink !== 'https://discord.gg/WzejVAx'">
|
||||
<a :href="discordLink" target="_blank">{{ discordName }}</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">The Modding Tree</a></li>
|
||||
<li><a href="http://discord.gg/wwQfgPa" target="_blank">Jacorb's Games</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div @click="openDialog('Info')">
|
||||
<tooltip display="<span>Info</span>" bottom class="info"><span>i</span></tooltip>
|
||||
</div>
|
||||
<div @click="openDialog('Saves')">
|
||||
<tooltip display="Saves" bottom class="saves" xoffset="-20px">
|
||||
<span class="material-icons">library_books</span>
|
||||
</tooltip>
|
||||
</div>
|
||||
<div @click="openDialog('Options')">
|
||||
<tooltip display="<span>Options</span>" bottom class="options" xoffset="-70px">
|
||||
<img src="images/options_wheel.png" />
|
||||
</tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="overlay-nav" v-bind="$attrs">
|
||||
<div @click="openDialog('Changelog')" class="version-container">
|
||||
<tooltip display="Changelog" right xoffset="25%" class="version"><span>v{{ version }}</span></tooltip>
|
||||
</div>
|
||||
<div @click="openDialog('Saves')">
|
||||
<tooltip display="Saves" right class="saves"><span class="material-icons">library_books</span></tooltip>
|
||||
</div>
|
||||
<div @click="openDialog('Options')">
|
||||
<tooltip display="<span>Options</span>" right class="options"><img src="images/options_wheel.png" /></tooltip>
|
||||
</div>
|
||||
<div @click="openDialog('Info')">
|
||||
<tooltip display="<span>Info</span>" right class="info"><span>i</span></tooltip>
|
||||
</div>
|
||||
<div class="discord">
|
||||
<img src="images/discord.png" @click="openDiscord" />
|
||||
<ul class="discord-links">
|
||||
<li v-if="discordLink !== 'https://discord.gg/WzejVAx'">
|
||||
<a :href="discordLink" target="_blank">{{ discordName }}</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">The Modding Tree</a></li>
|
||||
<li><a href="http://discord.gg/wwQfgPa" target="_blank">Jacorb's Games</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<Info :show="showInfo" @openDialog="openDialog" @closeDialog="closeDialog" />
|
||||
<SavesManager :show="showSaves" @closeDialog="closeDialog" />
|
||||
<Options :show="showOptions" @closeDialog="closeDialog" />
|
||||
<div class="nav" v-if="useHeader" v-bind="$attrs">
|
||||
<img v-if="banner" :src="banner" height="100%" :alt="title" />
|
||||
<div v-else class="title">{{ title }}</div>
|
||||
<div @click="openDialog('Changelog')" class="version-container">
|
||||
<tooltip display="Changelog" bottom class="version"
|
||||
><span>v{{ version }}</span></tooltip
|
||||
>
|
||||
</div>
|
||||
<div style="flex-grow: 1; cursor: unset;"></div>
|
||||
<div class="discord">
|
||||
<img src="images/discord.png" @click="window.open(discordLink, 'mywindow')" />
|
||||
<ul class="discord-links">
|
||||
<li v-if="discordLink !== 'https://discord.gg/WzejVAx'">
|
||||
<a :href="discordLink" target="_blank">{{ discordName }}</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">The Modding Tree</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://discord.gg/wwQfgPa" target="_blank">Jacorb's Games</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div @click="openDialog('Info')">
|
||||
<tooltip display="<span>Info</span>" bottom class="info"><span>i</span></tooltip>
|
||||
</div>
|
||||
<div @click="openDialog('Saves')">
|
||||
<tooltip display="Saves" bottom class="saves" xoffset="-20px">
|
||||
<span class="material-icons">library_books</span>
|
||||
</tooltip>
|
||||
</div>
|
||||
<div @click="openDialog('Options')">
|
||||
<tooltip display="<span>Options</span>" bottom class="options" xoffset="-70px">
|
||||
<img src="images/options_wheel.png" />
|
||||
</tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="overlay-nav" v-bind="$attrs">
|
||||
<div @click="openDialog('Changelog')" class="version-container">
|
||||
<tooltip display="Changelog" right xoffset="25%" class="version"
|
||||
><span>v{{ version }}</span></tooltip
|
||||
>
|
||||
</div>
|
||||
<div @click="openDialog('Saves')">
|
||||
<tooltip display="Saves" right class="saves"
|
||||
><span class="material-icons">library_books</span></tooltip
|
||||
>
|
||||
</div>
|
||||
<div @click="openDialog('Options')">
|
||||
<tooltip display="<span>Options</span>" right class="options"
|
||||
><img src="images/options_wheel.png"
|
||||
/></tooltip>
|
||||
</div>
|
||||
<div @click="openDialog('Info')">
|
||||
<tooltip display="<span>Info</span>" right class="info"><span>i</span></tooltip>
|
||||
</div>
|
||||
<div class="discord">
|
||||
<img src="images/discord.png" @click="openDiscord" />
|
||||
<ul class="discord-links">
|
||||
<li v-if="discordLink !== 'https://discord.gg/WzejVAx'">
|
||||
<a :href="discordLink" target="_blank">{{ discordName }}</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">The Modding Tree</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://discord.gg/wwQfgPa" target="_blank">Jacorb's Games</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<Info :show="showInfo" @openDialog="openDialog" @closeDialog="closeDialog" />
|
||||
<SavesManager :show="showSaves" @closeDialog="closeDialog" />
|
||||
<Options :show="showOptions" @closeDialog="closeDialog" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modInfo from '../../data/modInfo';
|
||||
<script lang="ts">
|
||||
import modInfo from "@/data/modInfo.json";
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'Nav',
|
||||
data() {
|
||||
return {
|
||||
useHeader: modInfo.useHeader,
|
||||
banner: modInfo.banner,
|
||||
title: modInfo.title,
|
||||
discordName: modInfo.discordName,
|
||||
discordLink: modInfo.discordLink,
|
||||
version: modInfo.versionNumber,
|
||||
showInfo: false,
|
||||
showSaves: false,
|
||||
showOptions: false,
|
||||
showChangelog: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openDiscord() {
|
||||
window.open(this.discordLink, 'mywindow');
|
||||
},
|
||||
openDialog(dialog) {
|
||||
this[`show${dialog}`] = true;
|
||||
},
|
||||
closeDialog(dialog) {
|
||||
this[`show${dialog}`] = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
type modals = "Info" | "Saves" | "Options" | "Changelog";
|
||||
type showModals = "showInfo" | "showSaves" | "showOptions" | "showChangelog";
|
||||
|
||||
export default defineComponent({
|
||||
name: "Nav",
|
||||
data() {
|
||||
return {
|
||||
useHeader: modInfo.useHeader,
|
||||
banner: modInfo.banner,
|
||||
title: modInfo.title,
|
||||
discordName: modInfo.discordName,
|
||||
discordLink: modInfo.discordLink,
|
||||
version: modInfo.versionNumber,
|
||||
showInfo: false,
|
||||
showSaves: false,
|
||||
showOptions: false,
|
||||
showChangelog: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
openDiscord() {
|
||||
window.open(this.discordLink, "mywindow");
|
||||
},
|
||||
openDialog(dialog: modals) {
|
||||
this[`show${dialog}` as showModals] = true;
|
||||
},
|
||||
closeDialog(dialog: modals) {
|
||||
this[`show${dialog}` as showModals] = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.nav {
|
||||
background-color: var(--secondary-background);
|
||||
display: flex;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 46px;
|
||||
width: 100%;
|
||||
border-bottom: 4px solid var(--separator);
|
||||
background-color: var(--secondary-background);
|
||||
display: flex;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 46px;
|
||||
width: 100%;
|
||||
border-bottom: 4px solid var(--separator);
|
||||
}
|
||||
|
||||
.nav > * {
|
||||
height: 46px;
|
||||
width: 46px;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.overlay-nav {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.overlay-nav > * {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36px;
|
||||
text-align: left;
|
||||
margin-left: 12px;
|
||||
cursor: unset;
|
||||
font-size: 36px;
|
||||
text-align: left;
|
||||
margin-left: 12px;
|
||||
cursor: unset;
|
||||
}
|
||||
|
||||
.nav > .title {
|
||||
width: unset;
|
||||
width: unset;
|
||||
}
|
||||
|
||||
.nav .saves,
|
||||
|
@ -149,36 +177,36 @@ export default {
|
|||
}
|
||||
|
||||
.tooltip-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.overlay-nav .discord {
|
||||
position: relative;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.discord img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.discord-links {
|
||||
position: fixed;
|
||||
top: 45px;
|
||||
padding: 20px;
|
||||
right: -280px;
|
||||
width: 200px;
|
||||
transition: right .25s ease;
|
||||
background: var(--secondary-background);
|
||||
z-index: 10;
|
||||
position: fixed;
|
||||
top: 45px;
|
||||
padding: 20px;
|
||||
right: -280px;
|
||||
width: 200px;
|
||||
transition: right 0.25s ease;
|
||||
background: var(--secondary-background);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.overlay-nav .discord-links {
|
||||
position: absolute;
|
||||
position: absolute;
|
||||
left: -280px;
|
||||
right: unset;
|
||||
transition: left .25s ease;
|
||||
transition: left 0.25s ease;
|
||||
}
|
||||
|
||||
.overlay-nav .discord:hover .discord-links {
|
||||
|
@ -186,7 +214,7 @@ export default {
|
|||
}
|
||||
|
||||
.discord-links li {
|
||||
margin-bottom: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.discord-links li:first-child {
|
||||
|
@ -194,38 +222,36 @@ export default {
|
|||
}
|
||||
|
||||
*:not(.overlay-nav) .discord:hover .discord-links {
|
||||
right: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.info {
|
||||
font-size: 30px;
|
||||
color: var(--link);
|
||||
line-height: 14px;
|
||||
font-size: 30px;
|
||||
color: var(--link);
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.info:hover span {
|
||||
transform: scale(1.2, 1.2);
|
||||
text-shadow: 5px 0 10px var(--link),
|
||||
-3px 0 12px var(--link);
|
||||
transform: scale(1.2, 1.2);
|
||||
text-shadow: 5px 0 10px var(--link), -3px 0 12px var(--link);
|
||||
}
|
||||
|
||||
.saves span {
|
||||
font-size: 36px;
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.saves:hover span {
|
||||
transform: scale(1.2, 1.2);
|
||||
text-shadow: 5px 0 10px var(--color),
|
||||
-3px 0 12px var(--color);
|
||||
transform: scale(1.2, 1.2);
|
||||
text-shadow: 5px 0 10px var(--color), -3px 0 12px var(--color);
|
||||
}
|
||||
|
||||
.options img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.options:hover img {
|
||||
transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
|
||||
.nav .version-container {
|
||||
|
@ -236,17 +262,17 @@ export default {
|
|||
}
|
||||
|
||||
.overlay-nav .version-container {
|
||||
width: unset;
|
||||
height: 25px;
|
||||
width: unset;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.version {
|
||||
color: var(--points);
|
||||
color: var(--points);
|
||||
}
|
||||
|
||||
.version:hover span {
|
||||
transform-origin: 0% 50%;
|
||||
transform: scale(1.2, 1.2);
|
||||
text-shadow: 5px 0 10px var(--points), -3px 0 12px var(--points);
|
||||
transform-origin: 0% 50%;
|
||||
transform: scale(1.2, 1.2);
|
||||
text-shadow: 5px 0 10px var(--points), -3px 0 12px var(--points);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,66 +1,93 @@
|
|||
<template>
|
||||
<Modal :show="show" @close="$emit('closeDialog', 'Options')">
|
||||
<template v-slot:header>
|
||||
<div class="header">
|
||||
<h2>Options</h2>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<Select title="Theme" :options="themes" :value="theme" @change="setTheme" default="classic" />
|
||||
<Select title="Show Milestones" :options="msDisplayOptions" :value="msDisplay" @change="setMSDisplay" default="all" />
|
||||
<Toggle title="Offline Production" :value="offlineProd" @change="toggleOption('offlineProd')" />
|
||||
<Toggle title="Autosave" :value="autosave" @change="toggleOption('autosave')" />
|
||||
<Toggle title="Pause game" :value="paused" @change="togglePaused" />
|
||||
<Toggle title="Show TPS" :value="showTPS" @change="toggleOption('showTPS')" />
|
||||
<Toggle title="Hide Maxed Challenges" :value="hideChallenges" @change="toggleOption('hideChallenges')" />
|
||||
</template>
|
||||
</Modal>
|
||||
<Modal :show="show" @close="$emit('closeDialog', 'Options')">
|
||||
<template v-slot:header>
|
||||
<div class="header">
|
||||
<h2>Options</h2>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<Select
|
||||
title="Theme"
|
||||
:options="themes"
|
||||
:value="theme"
|
||||
@change="setTheme"
|
||||
default="classic"
|
||||
/>
|
||||
<Select
|
||||
title="Show Milestones"
|
||||
:options="msDisplayOptions"
|
||||
:value="msDisplay"
|
||||
@change="setMSDisplay"
|
||||
default="all"
|
||||
/>
|
||||
<Toggle
|
||||
title="Offline Production"
|
||||
:value="offlineProd"
|
||||
@change="toggleOption('offlineProd')"
|
||||
/>
|
||||
<Toggle title="Autosave" :value="autosave" @change="toggleOption('autosave')" />
|
||||
<Toggle title="Pause game" :value="paused" @change="togglePaused" />
|
||||
<Toggle title="Show TPS" :value="showTPS" @change="toggleOption('showTPS')" />
|
||||
<Toggle
|
||||
title="Hide Maxed Challenges"
|
||||
:value="hideChallenges"
|
||||
@change="toggleOption('hideChallenges')"
|
||||
/>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import themes from '../../data/themes';
|
||||
import { camelToTitle } from '../../util/common';
|
||||
import { mapState } from '../../util/vue';
|
||||
import player from '../../game/player';
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import themes, { Themes } from "@/data/themes";
|
||||
import { camelToTitle } from "@/util/common";
|
||||
import { mapState } from "@/util/vue";
|
||||
import player from "@/game/player";
|
||||
import { MilestoneDisplay } from "@/game/enums";
|
||||
|
||||
export default {
|
||||
name: 'Options',
|
||||
props: {
|
||||
show: Boolean
|
||||
},
|
||||
emits: [ 'closeDialog' ],
|
||||
data() {
|
||||
return {
|
||||
themes: Object.keys(themes).map(theme => ({ label: camelToTitle(theme), value: theme })),
|
||||
msDisplayOptions: [ "all", "last", "configurable", "incomplete", "none" ]
|
||||
.map(option => ({ label: camelToTitle(option), value: option }))
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState([ "autosave", "offlineProd", "showTPS", "hideChallenges", "theme", "msDisplay" ]),
|
||||
paused() {
|
||||
return player.devSpeed === 0;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleOption(option) {
|
||||
player[option] = !player[option];
|
||||
},
|
||||
setTheme(theme) {
|
||||
player.theme = theme;
|
||||
},
|
||||
setMSDisplay(msDisplay) {
|
||||
player.msDisplay = msDisplay;
|
||||
},
|
||||
togglePaused() {
|
||||
player.devSpeed = this.paused ? 1 : 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "Options",
|
||||
props: {
|
||||
show: Boolean
|
||||
},
|
||||
emits: ["closeDialog"],
|
||||
data() {
|
||||
return {
|
||||
themes: Object.keys(themes).map(theme => ({
|
||||
label: camelToTitle(theme),
|
||||
value: theme
|
||||
})),
|
||||
msDisplayOptions: ["all", "last", "configurable", "incomplete", "none"].map(option => ({
|
||||
label: camelToTitle(option),
|
||||
value: option
|
||||
}))
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["autosave", "offlineProd", "showTPS", "hideChallenges", "theme", "msDisplay"]),
|
||||
paused() {
|
||||
return player.devSpeed === 0;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleOption(option: string) {
|
||||
player[option] = !player[option];
|
||||
},
|
||||
setTheme(theme: Themes) {
|
||||
player.theme = theme;
|
||||
},
|
||||
setMSDisplay(msDisplay: MilestoneDisplay) {
|
||||
player.msDisplay = msDisplay;
|
||||
},
|
||||
togglePaused() {
|
||||
player.devSpeed = this.paused ? 1 : 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
margin-bottom: -10px;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
<template>
|
||||
<h2 v-bind:style="{ color, 'text-shadow': '0px 0px 10px ' + color }">
|
||||
{{ amount }}
|
||||
</h2>
|
||||
<h2 v-bind:style="{ color, 'text-shadow': '0px 0px 10px ' + color }">
|
||||
{{ amount }}
|
||||
</h2>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'resource',
|
||||
props: {
|
||||
color: String,
|
||||
amount: String
|
||||
}
|
||||
};
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "resource",
|
||||
props: {
|
||||
color: String,
|
||||
amount: String
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
<template>
|
||||
<div class="table">
|
||||
<div class="row">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<div class="table">
|
||||
<div class="row">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'row'
|
||||
};
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "row"
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,102 +1,127 @@
|
|||
<template>
|
||||
<div class="save" :class="{ active }">
|
||||
<div class='handle material-icons'>drag_handle</div>
|
||||
<div class="actions" v-if="!editing">
|
||||
<feedback-button @click="$emit('export')" class="button" left v-if="save.error == undefined && !confirming">
|
||||
<span class="material-icons">content_paste</span>
|
||||
</feedback-button>
|
||||
<button @click="$emit('duplicate')" class="button" v-if="save.error == undefined && !confirming">
|
||||
<span class="material-icons">content_copy</span>
|
||||
</button>
|
||||
<button @click="toggleEditing" class="button" v-if="save.error == undefined && !confirming">
|
||||
<span class="material-icons">edit</span>
|
||||
</button>
|
||||
<danger-button :disabled="active" @click="$emit('delete')" @confirmingChanged="confirmingChanged">
|
||||
<span class="material-icons" style="margin: -2px">delete</span>
|
||||
</danger-button>
|
||||
</div>
|
||||
<div class="actions" v-else>
|
||||
<button @click="changeName" class="button">
|
||||
<span class="material-icons">check</span>
|
||||
</button>
|
||||
<button @click="toggleEditing" class="button">
|
||||
<span class="material-icons">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="details" v-if="save.error == undefined && !editing">
|
||||
<button class="button open" @click="$emit('open')">
|
||||
<h3>{{ save.name }}</h3>
|
||||
</button>
|
||||
<span class="save-version">v{{ save.modVersion }}</span><br>
|
||||
<div>Last played {{ dateFormat.format(time) }}</div>
|
||||
</div>
|
||||
<div class="details" v-else-if="save.error == undefined && editing">
|
||||
<TextField v-model="newName" class="editname" @submit="changeName" />
|
||||
</div>
|
||||
<div v-else class="details error">
|
||||
Error: Failed to load save with id {{ save.id }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="save" :class="{ active }">
|
||||
<div class="handle material-icons">drag_handle</div>
|
||||
<div class="actions" v-if="!editing">
|
||||
<feedback-button
|
||||
@click="$emit('export')"
|
||||
class="button"
|
||||
left
|
||||
v-if="save.error == undefined && !confirming"
|
||||
>
|
||||
<span class="material-icons">content_paste</span>
|
||||
</feedback-button>
|
||||
<button
|
||||
@click="$emit('duplicate')"
|
||||
class="button"
|
||||
v-if="save.error == undefined && !confirming"
|
||||
>
|
||||
<span class="material-icons">content_copy</span>
|
||||
</button>
|
||||
<button
|
||||
@click="toggleEditing"
|
||||
class="button"
|
||||
v-if="save.error == undefined && !confirming"
|
||||
>
|
||||
<span class="material-icons">edit</span>
|
||||
</button>
|
||||
<danger-button
|
||||
:disabled="active"
|
||||
@click="$emit('delete')"
|
||||
@confirmingChanged="confirmingChanged"
|
||||
>
|
||||
<span class="material-icons" style="margin: -2px">delete</span>
|
||||
</danger-button>
|
||||
</div>
|
||||
<div class="actions" v-else>
|
||||
<button @click="changeName" class="button">
|
||||
<span class="material-icons">check</span>
|
||||
</button>
|
||||
<button @click="toggleEditing" class="button">
|
||||
<span class="material-icons">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="details" v-if="save.error == undefined && !editing">
|
||||
<button class="button open" @click="$emit('open')">
|
||||
<h3>{{ save.name }}</h3>
|
||||
</button>
|
||||
<span class="save-version">v{{ save.modVersion }}</span
|
||||
><br />
|
||||
<div v-if="time">Last played {{ dateFormat.format(time) }}</div>
|
||||
</div>
|
||||
<div class="details" v-else-if="save.error == undefined && editing">
|
||||
<TextField v-model="newName" class="editname" @submit="changeName" />
|
||||
</div>
|
||||
<div v-else class="details error">Error: Failed to load save with id {{ save.id }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import player from '../../game/player';
|
||||
<script lang="ts">
|
||||
import player from "@/game/player";
|
||||
import { PlayerData } from "@/typings/player";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'save',
|
||||
props: {
|
||||
save: Object
|
||||
},
|
||||
emits: [ 'export', 'open', 'duplicate', 'delete', 'editSave' ],
|
||||
data() {
|
||||
return {
|
||||
dateFormat: new Intl.DateTimeFormat('en-US', {
|
||||
year: 'numeric', month: 'numeric', day: 'numeric',
|
||||
hour: 'numeric', minute: 'numeric', second: 'numeric',
|
||||
}),
|
||||
editing: false,
|
||||
confirming: false,
|
||||
newName: ""
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
active() {
|
||||
return this.save.id === player.id;
|
||||
},
|
||||
time() {
|
||||
return this.active ? player.time : this.save.time;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
confirmingChanged(confirming) {
|
||||
this.confirming = confirming;
|
||||
},
|
||||
toggleEditing() {
|
||||
this.newName = this.save.name;
|
||||
this.editing = !this.editing;
|
||||
},
|
||||
changeName() {
|
||||
this.$emit('editSave', this.newName);
|
||||
this.editing = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "save",
|
||||
props: {
|
||||
save: {
|
||||
type: Object as PropType<Partial<PlayerData>>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
emits: ["export", "open", "duplicate", "delete", "editSave"],
|
||||
data() {
|
||||
return {
|
||||
dateFormat: new Intl.DateTimeFormat("en-US", {
|
||||
year: "numeric",
|
||||
month: "numeric",
|
||||
day: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
second: "numeric"
|
||||
}),
|
||||
editing: false,
|
||||
confirming: false,
|
||||
newName: ""
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
active(): boolean {
|
||||
return this.save.id === player.id;
|
||||
},
|
||||
time(): number | undefined {
|
||||
return this.active ? player.time : this.save.time;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
confirmingChanged(confirming: boolean) {
|
||||
this.confirming = confirming;
|
||||
},
|
||||
toggleEditing() {
|
||||
this.newName = this.save.name || "";
|
||||
this.editing = !this.editing;
|
||||
},
|
||||
changeName() {
|
||||
this.$emit("editSave", this.newName);
|
||||
this.editing = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.save {
|
||||
position: relative;
|
||||
border: solid 4px var(--separator);
|
||||
padding: 4px;
|
||||
background: var(--secondary-background);
|
||||
margin: var(--feature-margin);
|
||||
display: flex;
|
||||
position: relative;
|
||||
border: solid 4px var(--separator);
|
||||
padding: 4px;
|
||||
background: var(--secondary-background);
|
||||
margin: var(--feature-margin);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
.save.active {
|
||||
border-color: var(--bought);
|
||||
border-color: var(--bought);
|
||||
}
|
||||
|
||||
.open {
|
||||
|
@ -106,66 +131,66 @@ export default {
|
|||
}
|
||||
|
||||
.handle {
|
||||
flex-grow: 0;
|
||||
margin-right: 8px;
|
||||
margin-left: 0;
|
||||
cursor: pointer;
|
||||
flex-grow: 0;
|
||||
margin-right: 8px;
|
||||
margin-left: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.details {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
flex-grow: 1;
|
||||
margin-right: 80px;
|
||||
}
|
||||
|
||||
.error {
|
||||
font-size: .8em;
|
||||
font-size: 0.8em;
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.save-version {
|
||||
margin-left: 4px;
|
||||
font-size: .7em;
|
||||
opacity: .7;
|
||||
margin-left: 4px;
|
||||
font-size: 0.7em;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.actions {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 4px;
|
||||
display: flex;
|
||||
padding: 4px;
|
||||
background: inherit;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 4px;
|
||||
display: flex;
|
||||
padding: 4px;
|
||||
background: inherit;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.editname {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.save button {
|
||||
transition-duration: 0s;
|
||||
transition-duration: 0s;
|
||||
}
|
||||
|
||||
.save .actions button {
|
||||
display: flex;
|
||||
font-size: 1.2em;
|
||||
display: flex;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.save .actions button .material-icons {
|
||||
font-size: unset;
|
||||
font-size: unset;
|
||||
}
|
||||
|
||||
.save .button.danger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.save .field {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,204 +1,290 @@
|
|||
<template>
|
||||
<Modal :show="show" @close="$emit('closeDialog', 'Saves')">
|
||||
<template v-slot:header>
|
||||
<h2>Saves Manager</h2>
|
||||
</template>
|
||||
<template v-slot:body v-sortable="{ update, handle: '.handle' }">
|
||||
<save v-for="(save, index) in saves" :key="index" :save="save" @open="openSave(save.id)" @export="exportSave(save.id)"
|
||||
@editSave="name => editSave(save.id, name)" @duplicate="duplicateSave(save.id)" @delete="deleteSave(save.id)" />
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<div class="modal-footer">
|
||||
<TextField :value="saveToImport" @submit="importSave" @input="importSave"
|
||||
title="Import Save" placeholder="Paste your save here!" :class="{ importingFailed }" />
|
||||
<div class="field">
|
||||
<span class="field-title">Create Save</span>
|
||||
<div class="field-buttons">
|
||||
<button class="button" @click="newSave">New Game</button>
|
||||
<Select v-if="Object.keys(bank).length > 0" :options="bank" closeOnSelect
|
||||
@change="newFromPreset" placeholder="Select preset" class="presets" :value="[]" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div style="flex-grow: 1"></div>
|
||||
<button class="button modal-default-button" @click="$emit('closeDialog', 'Saves')">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
<Modal :show="show" @close="$emit('closeDialog', 'Saves')">
|
||||
<template v-slot:header>
|
||||
<h2>Saves Manager</h2>
|
||||
</template>
|
||||
<template v-slot:body v-sortable="{ update, handle: '.handle' }">
|
||||
<save
|
||||
v-for="(save, index) in saves"
|
||||
:key="index"
|
||||
:save="save"
|
||||
@open="openSave(save.id)"
|
||||
@export="exportSave(save.id)"
|
||||
@editSave="name => editSave(save.id, name)"
|
||||
@duplicate="duplicateSave(save.id)"
|
||||
@delete="deleteSave(save.id)"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<div class="modal-footer">
|
||||
<TextField
|
||||
:value="saveToImport"
|
||||
@submit="importSave"
|
||||
@input="importSave"
|
||||
title="Import Save"
|
||||
placeholder="Paste your save here!"
|
||||
:class="{ importingFailed }"
|
||||
/>
|
||||
<div class="field">
|
||||
<span class="field-title">Create Save</span>
|
||||
<div class="field-buttons">
|
||||
<button class="button" @click="newSave">New Game</button>
|
||||
<Select
|
||||
v-if="Object.keys(bank).length > 0"
|
||||
:options="bank"
|
||||
closeOnSelect
|
||||
@change="newFromPreset"
|
||||
placeholder="Select preset"
|
||||
class="presets"
|
||||
:value="[]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div style="flex-grow: 1"></div>
|
||||
<button
|
||||
class="button modal-default-button"
|
||||
@click="$emit('closeDialog', 'Saves')"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { newSave, getUniqueID, loadSave, save } from '../../util/save';
|
||||
import player from '../../game/player';
|
||||
import modInfo from '../../data/modInfo.json';
|
||||
<script lang="ts">
|
||||
import modInfo from "@/data/modInfo.json";
|
||||
import player from "@/game/player";
|
||||
import { PlayerData } from "@/typings/player";
|
||||
import { getUniqueID, loadSave, newSave, save } from "@/util/save";
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'SavesManager',
|
||||
props: {
|
||||
show: Boolean
|
||||
},
|
||||
emits: [ 'closeDialog' ],
|
||||
data() {
|
||||
let bankContext = require.context('raw-loader!../../../saves', true, /\.txt$/);
|
||||
let bank = bankContext.keys().reduce((acc, curr) => {
|
||||
// .slice(2, -4) strips the leading ./ and the trailing .txt
|
||||
acc.push({ label: curr.slice(2, -4), value: bankContext(curr).default });
|
||||
return acc;
|
||||
}, []);
|
||||
return {
|
||||
importingFailed: false,
|
||||
saves: {}, // Gets populated when the modal is opened
|
||||
saveToImport: "",
|
||||
bank
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
show(newValue) {
|
||||
if (newValue) {
|
||||
this.loadSaveData();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadSaveData() {
|
||||
try {
|
||||
const { saves } = JSON.parse(decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)))));
|
||||
this.saves = saves.reduce((acc, curr) => {
|
||||
try {
|
||||
acc[curr] = JSON.parse(decodeURIComponent(escape(atob(localStorage.getItem(curr)))));
|
||||
acc[curr].id = curr;
|
||||
} catch(error) {
|
||||
console.warn(`Can't load save with id "${curr}"`, error);
|
||||
acc[curr] = { error, id: curr };
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
} catch(e) {
|
||||
this.saves = { [ player.id ]: player };
|
||||
const modData = { active: player.id, saves: [ player.id ] };
|
||||
localStorage.setItem(modInfo.id, btoa(unescape(encodeURIComponent(JSON.stringify(modData)))));
|
||||
}
|
||||
},
|
||||
exportSave(id) {
|
||||
let saveToExport;
|
||||
if (player.id === id) {
|
||||
save();
|
||||
saveToExport = player.saveToExport;
|
||||
} else {
|
||||
saveToExport = btoa(unescape(encodeURIComponent(JSON.stringify(this.saves[id]))));
|
||||
}
|
||||
export default defineComponent({
|
||||
name: "SavesManager",
|
||||
props: {
|
||||
show: Boolean
|
||||
},
|
||||
emits: ["closeDialog"],
|
||||
data() {
|
||||
let bankContext = require.context("raw-loader!../../../saves", true, /\.txt$/);
|
||||
let bank = bankContext
|
||||
.keys()
|
||||
.reduce((acc: Array<{ label: string; value: string }>, curr) => {
|
||||
// .slice(2, -4) strips the leading ./ and the trailing .txt
|
||||
acc.push({
|
||||
label: curr.slice(2, -4),
|
||||
value: bankContext(curr).default
|
||||
});
|
||||
return acc;
|
||||
}, []);
|
||||
return {
|
||||
importingFailed: false,
|
||||
saves: {}, // Gets populated when the modal is opened
|
||||
saveToImport: "",
|
||||
bank
|
||||
} as {
|
||||
importingFailed: boolean;
|
||||
saves: Record<string, Partial<PlayerData>>;
|
||||
saveToImport: string;
|
||||
bank: Array<{ label: string; value: string }>;
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
show(newValue) {
|
||||
if (newValue) {
|
||||
this.loadSaveData();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadSaveData() {
|
||||
try {
|
||||
const { saves } = JSON.parse(
|
||||
decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)!)))
|
||||
);
|
||||
this.saves = saves.reduce(
|
||||
(acc: Record<string, Partial<PlayerData>>, curr: string) => {
|
||||
try {
|
||||
acc[curr] = JSON.parse(
|
||||
decodeURIComponent(escape(atob(localStorage.getItem(curr)!)))
|
||||
);
|
||||
acc[curr].id = curr;
|
||||
} catch (error) {
|
||||
console.warn(`Can't load save with id "${curr}"`, error);
|
||||
acc[curr] = { error, id: curr };
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
} catch (e) {
|
||||
this.saves = { [player.id]: player };
|
||||
const modData = { active: player.id, saves: [player.id] };
|
||||
localStorage.setItem(
|
||||
modInfo.id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
||||
);
|
||||
}
|
||||
},
|
||||
exportSave(id: string) {
|
||||
let saveToExport;
|
||||
if (player.id === id) {
|
||||
save();
|
||||
saveToExport = player.saveToExport;
|
||||
} else {
|
||||
saveToExport = btoa(unescape(encodeURIComponent(JSON.stringify(this.saves[id]))));
|
||||
}
|
||||
|
||||
// Put on clipboard. Using the clipboard API asks for permissions and stuff
|
||||
const el = document.createElement("textarea");
|
||||
el.value = saveToExport;
|
||||
document.body.appendChild(el);
|
||||
el.select();
|
||||
el.setSelectionRange(0, 99999);
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(el);
|
||||
},
|
||||
duplicateSave(id) {
|
||||
if (player.id === id) {
|
||||
save();
|
||||
}
|
||||
// Put on clipboard. Using the clipboard API asks for permissions and stuff
|
||||
const el = document.createElement("textarea");
|
||||
el.value = saveToExport;
|
||||
document.body.appendChild(el);
|
||||
el.select();
|
||||
el.setSelectionRange(0, 99999);
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(el);
|
||||
},
|
||||
duplicateSave(id: string) {
|
||||
if (player.id === id) {
|
||||
save();
|
||||
}
|
||||
|
||||
const playerData = { ...this.saves[id], id: getUniqueID() };
|
||||
localStorage.setItem(playerData.id, btoa(unescape(encodeURIComponent(JSON.stringify(playerData)))));
|
||||
const playerData = { ...this.saves[id], id: getUniqueID() };
|
||||
localStorage.setItem(
|
||||
playerData.id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(playerData))))
|
||||
);
|
||||
|
||||
const modData = JSON.parse(decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)))));
|
||||
modData.saves.push(playerData.id);
|
||||
localStorage.setItem(modInfo.id, btoa(unescape(encodeURIComponent(JSON.stringify(modData)))));
|
||||
this.saves[playerData.id] = playerData;
|
||||
},
|
||||
deleteSave(id) {
|
||||
const modData = JSON.parse(decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)))));
|
||||
modData.saves = modData.saves.filter(save => save !== id);
|
||||
localStorage.removeItem(id);
|
||||
localStorage.setItem(modInfo.id, btoa(unescape(encodeURIComponent(JSON.stringify(modData)))));
|
||||
delete this.saves[id];
|
||||
},
|
||||
openSave(id) {
|
||||
this.saves[player.id].time = player.time;
|
||||
loadSave(this.saves[id]);
|
||||
const modData = JSON.parse(decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)))));
|
||||
modData.active = id;
|
||||
localStorage.setItem(modInfo.id, btoa(unescape(encodeURIComponent(JSON.stringify(modData)))));
|
||||
},
|
||||
async newSave() {
|
||||
const playerData = await newSave();
|
||||
this.saves[playerData.id] = playerData;
|
||||
},
|
||||
newFromPreset(preset) {
|
||||
const playerData = JSON.parse(decodeURIComponent(escape(atob(preset))));
|
||||
playerData.id = getUniqueID();
|
||||
localStorage.setItem(playerData.id, btoa(unescape(encodeURIComponent(JSON.stringify(playerData)))));
|
||||
const modData = JSON.parse(
|
||||
decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)!)))
|
||||
);
|
||||
modData.saves.push(playerData.id);
|
||||
localStorage.setItem(
|
||||
modInfo.id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
||||
);
|
||||
this.saves[playerData.id] = playerData;
|
||||
},
|
||||
deleteSave(id: string) {
|
||||
const modData = JSON.parse(
|
||||
decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)!)))
|
||||
);
|
||||
modData.saves = modData.saves.filter((save: string) => save !== id);
|
||||
localStorage.removeItem(id);
|
||||
localStorage.setItem(
|
||||
modInfo.id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
||||
);
|
||||
delete this.saves[id];
|
||||
},
|
||||
openSave(id: string) {
|
||||
this.saves[player.id].time = player.time;
|
||||
loadSave(this.saves[id]);
|
||||
const modData = JSON.parse(
|
||||
decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)!)))
|
||||
);
|
||||
modData.active = id;
|
||||
localStorage.setItem(
|
||||
modInfo.id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
||||
);
|
||||
},
|
||||
newSave() {
|
||||
const playerData = newSave();
|
||||
this.saves[playerData.id] = playerData;
|
||||
},
|
||||
newFromPreset(preset: string) {
|
||||
const playerData = JSON.parse(decodeURIComponent(escape(atob(preset))));
|
||||
playerData.id = getUniqueID();
|
||||
localStorage.setItem(
|
||||
playerData.id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(playerData))))
|
||||
);
|
||||
|
||||
const modData = JSON.parse(decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)))));
|
||||
modData.saves.push(playerData.id);
|
||||
localStorage.setItem(modInfo.id, btoa(unescape(encodeURIComponent(JSON.stringify(modData)))));
|
||||
this.saves[playerData.id] = playerData;
|
||||
},
|
||||
editSave(id, newName) {
|
||||
this.saves[id].name = newName;
|
||||
if (player.id === id) {
|
||||
player.name = newName;
|
||||
save();
|
||||
} else {
|
||||
localStorage.setItem(id, btoa(unescape(encodeURIComponent(JSON.stringify(this.saves[id])))));
|
||||
}
|
||||
},
|
||||
importSave(text) {
|
||||
this.saveToImport = text;
|
||||
if (text) {
|
||||
this.$nextTick(() => {
|
||||
try {
|
||||
const playerData = JSON.parse(decodeURIComponent(escape(atob(text))));
|
||||
const id = getUniqueID();
|
||||
playerData.id = id;
|
||||
localStorage.setItem(id, btoa(unescape(encodeURIComponent(JSON.stringify(playerData)))));
|
||||
this.saves[id] = playerData;
|
||||
this.saveToImport = "";
|
||||
this.importingFailed = false;
|
||||
const modData = JSON.parse(
|
||||
decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)!)))
|
||||
);
|
||||
modData.saves.push(playerData.id);
|
||||
localStorage.setItem(
|
||||
modInfo.id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
||||
);
|
||||
this.saves[playerData.id] = playerData;
|
||||
},
|
||||
editSave(id: string, newName: string) {
|
||||
this.saves[id].name = newName;
|
||||
if (player.id === id) {
|
||||
player.name = newName;
|
||||
save();
|
||||
} else {
|
||||
localStorage.setItem(
|
||||
id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(this.saves[id]))))
|
||||
);
|
||||
}
|
||||
},
|
||||
importSave(text: string) {
|
||||
this.saveToImport = text;
|
||||
if (text) {
|
||||
this.$nextTick(() => {
|
||||
try {
|
||||
const playerData = JSON.parse(decodeURIComponent(escape(atob(text))));
|
||||
const id = getUniqueID();
|
||||
playerData.id = id;
|
||||
localStorage.setItem(
|
||||
id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(playerData))))
|
||||
);
|
||||
this.saves[id] = playerData;
|
||||
this.saveToImport = "";
|
||||
this.importingFailed = false;
|
||||
|
||||
const modData = JSON.parse(decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)))));
|
||||
modData.saves.push(id);
|
||||
localStorage.setItem(modInfo.id, btoa(unescape(encodeURIComponent(JSON.stringify(modData)))));
|
||||
} catch (e) {
|
||||
this.importingFailed = true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.importingFailed = false;
|
||||
}
|
||||
},
|
||||
update(e) {
|
||||
this.saves.splice(e.newIndex, 0, this.saves.splive(e.oldIndex, 1)[0]);
|
||||
|
||||
const modData = JSON.parse(decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)))));
|
||||
modData.saves.splice(e.newIndex, 0, modData.saves.splice(e.oldIndex, 1)[0]);
|
||||
localStorage.setItem(modInfo.id, btoa(unescape(encodeURIComponent(JSON.stringify(modData)))));
|
||||
}
|
||||
}
|
||||
};
|
||||
const modData = JSON.parse(
|
||||
decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)!)))
|
||||
);
|
||||
modData.saves.push(id);
|
||||
localStorage.setItem(
|
||||
modInfo.id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
||||
);
|
||||
} catch (e) {
|
||||
this.importingFailed = true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.importingFailed = false;
|
||||
}
|
||||
},
|
||||
update(e: { newIndex: number; oldIndex: number }) {
|
||||
const modData = JSON.parse(
|
||||
decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)!)))
|
||||
);
|
||||
modData.saves.splice(e.newIndex, 0, modData.saves.splice(e.oldIndex, 1)[0]);
|
||||
localStorage.setItem(
|
||||
modInfo.id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.field form,
|
||||
.field .field-title,
|
||||
.field .field-buttons {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.field-buttons {
|
||||
display: flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.field-buttons .field {
|
||||
margin: 0;
|
||||
margin-left: 8px;
|
||||
margin: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
|
@ -206,25 +292,21 @@ export default {
|
|||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.importingFailed input {
|
||||
color: red;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.field-buttons .v-select {
|
||||
width: 220px;
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.presets .vue-dropdown {
|
||||
|
||||
}
|
||||
|
||||
.presets .vue-select[aria-expanded='true'] vue-dropdown {
|
||||
visibility: hidden;
|
||||
.presets .vue-select[aria-expanded="true"] vue-dropdown {
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
<template>
|
||||
<div :style="{ width: spacingWidth, height: spacingHeight }"></div>
|
||||
<div :style="{ width: spacingWidth, height: spacingHeight }"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'spacer',
|
||||
props: {
|
||||
width: String,
|
||||
height: String
|
||||
},
|
||||
computed: {
|
||||
spacingWidth() {
|
||||
return this.width || '8px';
|
||||
},
|
||||
spacingHeight() {
|
||||
return this.height || '17px';
|
||||
}
|
||||
}
|
||||
};
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "spacer",
|
||||
props: {
|
||||
width: String,
|
||||
height: String
|
||||
},
|
||||
computed: {
|
||||
spacingWidth(): string {
|
||||
return this.width || "8px";
|
||||
},
|
||||
spacingHeight(): string {
|
||||
return this.height || "17px";
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,64 +1,73 @@
|
|||
<template>
|
||||
<div class="sticky" :style="{ top }" ref="sticky" data-v-sticky>
|
||||
<slot />
|
||||
</div>
|
||||
<div class="sticky" :style="{ top }" ref="sticky" data-v-sticky>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'sticky',
|
||||
data() {
|
||||
return {
|
||||
top: 0,
|
||||
observer: null
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.sticky == undefined) {
|
||||
this.$nextTick(this.mounted);
|
||||
} else {
|
||||
this.updateTop();
|
||||
this.observer = new ResizeObserver(this.updateTop);
|
||||
this.observer.observe(this.$refs.sticky.parentElement);
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
updateTop() {
|
||||
let el = this.$refs.sticky;
|
||||
if (el == undefined) {
|
||||
return;
|
||||
}
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
let top = 0;
|
||||
while (el.previousSibling) {
|
||||
if (el.previousSibling.dataset && 'vSticky' in el.previousSibling.dataset) {
|
||||
top += el.previousSibling.offsetHeight;
|
||||
}
|
||||
el = el.previousSibling;
|
||||
}
|
||||
this.top = top + "px";
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "sticky",
|
||||
data() {
|
||||
return {
|
||||
top: "0",
|
||||
observer: null
|
||||
} as {
|
||||
top: string;
|
||||
observer: ResizeObserver | null;
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.setup();
|
||||
},
|
||||
methods: {
|
||||
setup() {
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.sticky == undefined) {
|
||||
this.$nextTick(this.setup);
|
||||
} else {
|
||||
this.updateTop();
|
||||
this.observer = new ResizeObserver(this.updateTop);
|
||||
this.observer.observe((this.$refs.sticky as HTMLElement).parentElement!);
|
||||
}
|
||||
});
|
||||
},
|
||||
updateTop() {
|
||||
let el = this.$refs.sticky as HTMLElement;
|
||||
if (el == undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let top = 0;
|
||||
while (el.previousSibling) {
|
||||
const sibling = el.previousSibling as HTMLElement;
|
||||
if (sibling.dataset && "vSticky" in sibling.dataset) {
|
||||
top += sibling.offsetHeight;
|
||||
}
|
||||
el = sibling;
|
||||
}
|
||||
this.top = top + "px";
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sticky {
|
||||
position: sticky;
|
||||
background: var(--background);
|
||||
margin-left: -7px;
|
||||
margin-right: -7px;
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
z-index: 3;
|
||||
position: sticky;
|
||||
background: var(--background);
|
||||
margin-left: -7px;
|
||||
margin-right: -7px;
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.modal-body .sticky {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
</style>
|
|
@ -1,27 +0,0 @@
|
|||
<template>
|
||||
<LayerProvider :layer="layer || tab.layer" :index="tab.index">
|
||||
<component :is="display" />
|
||||
</LayerProvider>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
|
||||
export default {
|
||||
name: 'subtab',
|
||||
inject: [ 'tab' ],
|
||||
props: {
|
||||
layer: String,
|
||||
id: String
|
||||
},
|
||||
computed: {
|
||||
display() {
|
||||
return coerceComponent(layers[this.layer || this.tab.layer].subtabs[this.id].display);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
|
@ -1,28 +1,32 @@
|
|||
<template>
|
||||
<div class="tpsDisplay" v-if="tps !== 'NaN'">
|
||||
TPS: {{ tps }}
|
||||
</div>
|
||||
<div class="tpsDisplay" v-if="tps !== 'NaN'">TPS: {{ tps }}</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Decimal, { formatWhole } from '../../util/bignum';
|
||||
import player from '../../game/player';
|
||||
<script lang="ts">
|
||||
import player from "@/game/player";
|
||||
import Decimal, { formatWhole } from "@/util/bignum";
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'TPS',
|
||||
computed: {
|
||||
tps() {
|
||||
return formatWhole(Decimal.div(player.lastTenTicks.length, player.lastTenTicks.reduce((acc, curr) => acc + curr, 0)))
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "TPS",
|
||||
computed: {
|
||||
tps() {
|
||||
return formatWhole(
|
||||
Decimal.div(
|
||||
player.lastTenTicks.length,
|
||||
player.lastTenTicks.reduce((acc, curr) => acc + curr, 0)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tpsDisplay {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
bottom: 10px;
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
bottom: 10px;
|
||||
z-index: 100;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,50 +1,70 @@
|
|||
<template>
|
||||
<button @click="$emit('selectTab')" class="tabButton" :style="style"
|
||||
:class="{ notify: options.notify, resetNotify: options.resetNotify, floating, activeTab }">
|
||||
{{ text }}
|
||||
</button>
|
||||
<button
|
||||
@click="$emit('selectTab')"
|
||||
class="tabButton"
|
||||
:style="style"
|
||||
:class="{
|
||||
notify: options.notify,
|
||||
resetNotify: options.resetNotify,
|
||||
floating,
|
||||
activeTab
|
||||
}"
|
||||
>
|
||||
{{ text }}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import player from '../../game/player';
|
||||
import themes from '../../data/themes';
|
||||
<script lang="ts">
|
||||
import themes from "@/data/themes";
|
||||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { Subtab } from "@/typings/features/subtab";
|
||||
import { InjectLayerMixin } from "@/util/vue";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'tab-button',
|
||||
props: {
|
||||
layer: String,
|
||||
text: String,
|
||||
options: Object,
|
||||
activeTab: Boolean
|
||||
},
|
||||
emits: [ 'selectTab' ],
|
||||
inject: [ 'tab' ],
|
||||
computed: {
|
||||
floating() {
|
||||
return themes[player.theme].floatingTabs;
|
||||
},
|
||||
style() {
|
||||
return [
|
||||
(this.floating || this.activeTab) && { 'border-color': layers[this.layer || this.tab.layer].color },
|
||||
layers[this.layer || this.tab.layer].componentStyles?.['tab-button'],
|
||||
this.options.resetNotify && this.options.glowColor &&
|
||||
{
|
||||
boxShadow: this.floating ?
|
||||
`-2px -4px 4px rgba(0, 0, 0, 0) inset, 0 0 8px ${this.options.glowColor}` :
|
||||
`0px 10px 7px -10px ${this.options.glowColor}`
|
||||
},
|
||||
this.options.notify && this.options.glowColor &&
|
||||
{
|
||||
boxShadow: this.floating ?
|
||||
`-2px -4px 4px rgba(0, 0, 0, 0) inset, 0 0 20px ${this.options.glowColor}` :
|
||||
`0px 15px 7px -10px ${this.options.glowColor}`
|
||||
},
|
||||
this.options.buttonStyle
|
||||
];
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "tab-button",
|
||||
mixins: [InjectLayerMixin],
|
||||
props: {
|
||||
text: String,
|
||||
options: {
|
||||
type: Object as PropType<Subtab>,
|
||||
required: true
|
||||
},
|
||||
activeTab: Boolean
|
||||
},
|
||||
emits: ["selectTab"],
|
||||
computed: {
|
||||
floating(): boolean {
|
||||
return themes[player.theme].floatingTabs;
|
||||
},
|
||||
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [
|
||||
this.floating || this.activeTab
|
||||
? {
|
||||
borderColor: layers[this.layer].color
|
||||
}
|
||||
: undefined,
|
||||
layers[this.layer].componentStyles?.["tab-button"],
|
||||
this.options.resetNotify && this.options.glowColor
|
||||
? {
|
||||
boxShadow: this.floating
|
||||
? `-2px -4px 4px rgba(0, 0, 0, 0) inset, 0 0 8px ${this.options.glowColor}`
|
||||
: `0px 10px 7px -10px ${this.options.glowColor}`
|
||||
}
|
||||
: undefined,
|
||||
this.options.notify && this.options.glowColor
|
||||
? {
|
||||
boxShadow: this.floating
|
||||
? `-2px -4px 4px rgba(0, 0, 0, 0) inset, 0 0 20px ${this.options.glowColor}`
|
||||
: `0px 15px 7px -10px ${this.options.glowColor}`
|
||||
}
|
||||
: undefined,
|
||||
this.options.buttonStyle
|
||||
];
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -66,17 +86,17 @@ export default {
|
|||
}
|
||||
|
||||
.tabButton:not(.floating) {
|
||||
height: 50px;
|
||||
margin: 0;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
border-bottom-width: 4px;
|
||||
border-radius: 0;
|
||||
transform: unset;
|
||||
height: 50px;
|
||||
margin: 0;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
border-bottom-width: 4px;
|
||||
border-radius: 0;
|
||||
transform: unset;
|
||||
}
|
||||
|
||||
.tabButton:not(.floating):not(.activeTab) {
|
||||
border-bottom-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,45 +1,60 @@
|
|||
<template>
|
||||
<div class="tabs-container">
|
||||
<div v-for="(tab, index) in tabs" :key="index" class="tab" :ref="`tab-${index}`">
|
||||
<Nav v-if="index === 0 && !useHeader" />
|
||||
<div class="inner-tab">
|
||||
<LayerProvider :layer="tab" :index="index" v-if="tab in components && components[tab]">
|
||||
<component :is="components[tab]" />
|
||||
</LayerProvider>
|
||||
<layer-tab :layer="tab" :index="index" v-else-if="tab in components" :minimizable="true"
|
||||
:tab="() => $refs[`tab-${index}`]" />
|
||||
<component :is="tab" :index="index" v-else />
|
||||
</div>
|
||||
<div class="separator" v-if="index !== tabs.length - 1"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabs-container">
|
||||
<div v-for="(tab, index) in tabs" :key="index" class="tab" :ref="`tab-${index}`">
|
||||
<Nav v-if="index === 0 && !useHeader" />
|
||||
<div class="inner-tab">
|
||||
<LayerProvider
|
||||
:layer="tab"
|
||||
:index="index"
|
||||
v-if="tab in components && components[tab]"
|
||||
>
|
||||
<component :is="components[tab]" />
|
||||
</LayerProvider>
|
||||
<layer-tab
|
||||
:layer="tab"
|
||||
:index="index"
|
||||
v-else-if="tab in components"
|
||||
:minimizable="true"
|
||||
:tab="() => $refs[`tab-${index}`]"
|
||||
/>
|
||||
<component :is="tab" :index="index" v-else />
|
||||
</div>
|
||||
<div class="separator" v-if="index !== tabs.length - 1"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modInfo from '../../data/modInfo.json';
|
||||
import { layers } from '../../game/layers';
|
||||
import { mapState } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import modInfo from "@/data/modInfo.json";
|
||||
import { layers } from "@/game/layers";
|
||||
import { coerceComponent, mapState } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'Tabs',
|
||||
data() {
|
||||
return { useHeader: modInfo.useHeader };
|
||||
},
|
||||
computed: {
|
||||
...mapState([ 'tabs' ]),
|
||||
components() {
|
||||
return Object.keys(layers).reduce((acc, curr) => {
|
||||
acc[curr] = layers[curr].component || false;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "Tabs",
|
||||
data() {
|
||||
return { useHeader: modInfo.useHeader };
|
||||
},
|
||||
computed: {
|
||||
...mapState(["tabs"]),
|
||||
components() {
|
||||
return Object.keys(layers).reduce(
|
||||
(acc: Record<string, Component | string | false>, curr) => {
|
||||
acc[curr] =
|
||||
(layers[curr].component && coerceComponent(layers[curr].component!)) ||
|
||||
false;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tabs-container {
|
||||
width: 100vw;
|
||||
width: 100vw;
|
||||
flex-grow: 1;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
|
@ -70,12 +85,12 @@ export default {
|
|||
}
|
||||
|
||||
.separator {
|
||||
position: absolute;
|
||||
right: -3px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 6px;
|
||||
background: var(--separator);
|
||||
position: absolute;
|
||||
right: -3px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 6px;
|
||||
background: var(--separator);
|
||||
z-index: 1;
|
||||
}
|
||||
</style>
|
||||
|
@ -89,6 +104,6 @@ export default {
|
|||
}
|
||||
|
||||
.tab .modal-body hr {
|
||||
margin: 7px 0;
|
||||
margin: 7px 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,66 +1,84 @@
|
|||
<template>
|
||||
<div class="tooltip-container" :class="{ shown }" @mouseenter="setHover(true)" @mouseleave="setHover(false)">
|
||||
<slot />
|
||||
<transition name="fade">
|
||||
<div v-if="shown" class="tooltip" :class="{ top, left, right, bottom }"
|
||||
:style="{ '--xoffset': xoffset || '0px', '--yoffset': yoffset || '0px' }">
|
||||
<component :is="tooltipDisplay" />
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
<div
|
||||
class="tooltip-container"
|
||||
:class="{ shown }"
|
||||
@mouseenter="setHover(true)"
|
||||
@mouseleave="setHover(false)"
|
||||
>
|
||||
<slot />
|
||||
<transition name="fade">
|
||||
<div
|
||||
v-if="shown"
|
||||
class="tooltip"
|
||||
:class="{ top, left, right, bottom }"
|
||||
:style="{
|
||||
'--xoffset': xoffset || '0px',
|
||||
'--yoffset': yoffset || '0px'
|
||||
}"
|
||||
>
|
||||
<component :is="tooltipDisplay" />
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { CoercableComponent } from "@/typings/component";
|
||||
import { coerceComponent } from "@/util/vue";
|
||||
import { Component, defineComponent, PropType } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'tooltip',
|
||||
data() {
|
||||
return {
|
||||
hover: false
|
||||
};
|
||||
},
|
||||
props: {
|
||||
force: Boolean,
|
||||
display: String,
|
||||
top: Boolean,
|
||||
left: Boolean,
|
||||
right: Boolean,
|
||||
bottom: Boolean,
|
||||
xoffset: String,
|
||||
yoffset: String
|
||||
},
|
||||
computed: {
|
||||
tooltipDisplay() {
|
||||
return coerceComponent(this.display);
|
||||
},
|
||||
shown() {
|
||||
return this.force || this.hover;
|
||||
}
|
||||
},
|
||||
provide: {
|
||||
tab() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setHover(hover) {
|
||||
this.hover = hover;
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "tooltip",
|
||||
data() {
|
||||
return {
|
||||
hover: false
|
||||
};
|
||||
},
|
||||
props: {
|
||||
force: Boolean,
|
||||
display: {
|
||||
type: [String, Object] as PropType<CoercableComponent>,
|
||||
required: true
|
||||
},
|
||||
top: Boolean,
|
||||
left: Boolean,
|
||||
right: Boolean,
|
||||
bottom: Boolean,
|
||||
xoffset: String,
|
||||
yoffset: String
|
||||
},
|
||||
computed: {
|
||||
tooltipDisplay(): Component | string {
|
||||
return coerceComponent(this.display);
|
||||
},
|
||||
shown(): boolean {
|
||||
return this.force || this.hover;
|
||||
}
|
||||
},
|
||||
provide: {
|
||||
tab() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setHover(hover: boolean) {
|
||||
this.hover = hover;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tooltip-container {
|
||||
position: relative;
|
||||
--xoffset: 0px;
|
||||
--yoffset: 0px;
|
||||
--xoffset: 0px;
|
||||
--yoffset: 0px;
|
||||
}
|
||||
|
||||
.tooltip, .tooltip::after {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
.tooltip,
|
||||
.tooltip::after {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
|
@ -76,80 +94,81 @@ export default {
|
|||
border-radius: 3px;
|
||||
background-color: var(--background-tooltip);
|
||||
color: var(--points);
|
||||
z-index: 100 !important;
|
||||
z-index: 100 !important;
|
||||
}
|
||||
|
||||
.shown {
|
||||
z-index: 10;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.fade-enter, .fade-leave-to {
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.tooltip::after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
bottom: 100%;
|
||||
left: calc(50% - var(--xoffset));
|
||||
width: 0;
|
||||
margin-left: -5px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: var(--background-tooltip) transparent transparent transparent;
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
bottom: 100%;
|
||||
left: calc(50% - var(--xoffset));
|
||||
width: 0;
|
||||
margin-left: -5px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: var(--background-tooltip) transparent transparent transparent;
|
||||
}
|
||||
|
||||
.tooltip.left,
|
||||
.side-nodes .tooltip:not(.right):not(.bottom):not(.top) {
|
||||
bottom: calc(50% + var(--yoffset));
|
||||
left: unset;
|
||||
right: calc(100% + var(--xoffset));
|
||||
margin-bottom: unset;
|
||||
margin-right: 5px;
|
||||
bottom: calc(50% + var(--yoffset));
|
||||
left: unset;
|
||||
right: calc(100% + var(--xoffset));
|
||||
margin-bottom: unset;
|
||||
margin-right: 5px;
|
||||
transform: translateY(50%);
|
||||
}
|
||||
|
||||
.tooltip.left::after,
|
||||
.side-nodes .tooltip:not(.right):not(.bottom):not(.top)::after {
|
||||
top: calc(50% + var(--yoffset));
|
||||
bottom: unset;
|
||||
left: 100%;
|
||||
right: 100%;
|
||||
margin-left: unset;
|
||||
margin-top: -5px;
|
||||
border-color: transparent transparent transparent var(--background-tooltip);
|
||||
top: calc(50% + var(--yoffset));
|
||||
bottom: unset;
|
||||
left: 100%;
|
||||
right: 100%;
|
||||
margin-left: unset;
|
||||
margin-top: -5px;
|
||||
border-color: transparent transparent transparent var(--background-tooltip);
|
||||
}
|
||||
|
||||
.tooltip.right {
|
||||
bottom: calc(50% + var(--yoffset));
|
||||
left: calc(100% + var(--xoffset));
|
||||
margin-bottom: unset;
|
||||
margin-left: 5px;
|
||||
bottom: calc(50% + var(--yoffset));
|
||||
left: calc(100% + var(--xoffset));
|
||||
margin-bottom: unset;
|
||||
margin-left: 5px;
|
||||
transform: translateY(50%);
|
||||
}
|
||||
|
||||
.tooltip.right::after {
|
||||
top: calc(50% + var(--yoffset));
|
||||
left: 0;
|
||||
right: 100%;
|
||||
margin-left: -10px;
|
||||
margin-top: -5px;
|
||||
border-color: transparent var(--background-tooltip) transparent transparent;
|
||||
top: calc(50% + var(--yoffset));
|
||||
left: 0;
|
||||
right: 100%;
|
||||
margin-left: -10px;
|
||||
margin-top: -5px;
|
||||
border-color: transparent var(--background-tooltip) transparent transparent;
|
||||
}
|
||||
|
||||
.tooltip.bottom {
|
||||
top: calc(100% + var(--yoffset));
|
||||
bottom: unset;
|
||||
left: calc(50% + var(--xoffset));
|
||||
margin-bottom: unset;
|
||||
margin-top: 5px;
|
||||
top: calc(100% + var(--yoffset));
|
||||
bottom: unset;
|
||||
left: calc(50% + var(--xoffset));
|
||||
margin-bottom: unset;
|
||||
margin-top: 5px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.tooltip.bottom::after {
|
||||
top: 0;
|
||||
margin-top: -10px;
|
||||
border-color: transparent transparent var(--background-tooltip) transparent;
|
||||
top: 0;
|
||||
margin-top: -10px;
|
||||
border-color: transparent transparent var(--background-tooltip) transparent;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
<template>
|
||||
<div class="vr" :style="{ height }"></div>
|
||||
<div class="vr" :style="{ height }"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'vr',
|
||||
props: {
|
||||
height: String
|
||||
}
|
||||
};
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "vr",
|
||||
props: {
|
||||
height: String
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.vr {
|
||||
width: 4px;
|
||||
background: var(--separator);
|
||||
height: 100%;
|
||||
margin: 0 7px;
|
||||
width: 4px;
|
||||
background: var(--separator);
|
||||
height: 100%;
|
||||
margin: 0 7px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,48 +1,66 @@
|
|||
<template>
|
||||
<line :stroke="stroke" :stroke-width="strokeWidth" v-bind="typeof options === 'string' ? [] : options"
|
||||
:x1="startPosition.x" :y1="startPosition.y" :x2="endPosition.x" :y2="endPosition.y" />
|
||||
<line
|
||||
:stroke="stroke"
|
||||
:stroke-width="strokeWidth"
|
||||
v-bind="typeof options === 'string' ? [] : options"
|
||||
:x1="startPosition.x"
|
||||
:y1="startPosition.y"
|
||||
:x2="endPosition.x"
|
||||
:y2="endPosition.y"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'branch-line',
|
||||
props: {
|
||||
options: [ String, Object ],
|
||||
startNode: Object,
|
||||
endNode: Object
|
||||
},
|
||||
computed: {
|
||||
stroke() {
|
||||
if (typeof this.options === 'string' || !('stroke' in this.options)) {
|
||||
return 'white';
|
||||
}
|
||||
return this.options.stroke;
|
||||
},
|
||||
strokeWidth() {
|
||||
if (typeof this.options === 'string' || !('stroke-width' in this.options)) {
|
||||
return '15px';
|
||||
}
|
||||
return this.options['stroke-width'];
|
||||
},
|
||||
startPosition() {
|
||||
const position = { x: this.startNode.x || 0, y: this.startNode.y || 0 };
|
||||
if (typeof this.options !== 'string' && 'startOffset' in this.options) {
|
||||
position.x += this.options.startOffset.x || 0;
|
||||
position.y += this.options.startOffset.y || 0;
|
||||
}
|
||||
return position;
|
||||
},
|
||||
endPosition() {
|
||||
const position = { x: this.endNode.x || 0, y: this.endNode.y || 0 };
|
||||
if (typeof this.options !== 'string' && 'endOffset' in this.options) {
|
||||
position.x += this.options.endOffset.x || 0;
|
||||
position.y += this.options.endOffset.y || 0;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
}
|
||||
};
|
||||
<script lang="ts">
|
||||
import { BranchNode, BranchOptions, Position } from "@/typings/branches";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "branch-line",
|
||||
props: {
|
||||
options: {
|
||||
type: [String, Object] as PropType<string | BranchOptions>,
|
||||
required: true
|
||||
},
|
||||
startNode: {
|
||||
type: Object as PropType<BranchNode>,
|
||||
required: true
|
||||
},
|
||||
endNode: {
|
||||
type: Object as PropType<BranchNode>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
stroke(): string {
|
||||
if (typeof this.options === "string" || !("stroke" in this.options)) {
|
||||
return "white";
|
||||
}
|
||||
return this.options.stroke!;
|
||||
},
|
||||
strokeWidth(): string {
|
||||
if (typeof this.options === "string" || !("stroke-width" in this.options)) {
|
||||
return "15px";
|
||||
}
|
||||
return this.options["stroke-width"]!;
|
||||
},
|
||||
startPosition(): Position {
|
||||
const position = { x: this.startNode.x || 0, y: this.startNode.y || 0 };
|
||||
if (typeof this.options !== "string" && "startOffset" in this.options) {
|
||||
position.x += this.options.startOffset?.x || 0;
|
||||
position.y += this.options.startOffset?.y || 0;
|
||||
}
|
||||
return position;
|
||||
},
|
||||
endPosition(): Position {
|
||||
const position = { x: this.endNode.x || 0, y: this.endNode.y || 0 };
|
||||
if (typeof this.options !== "string" && "endOffset" in this.options) {
|
||||
position.x += this.options.endOffset?.x || 0;
|
||||
position.y += this.options.endOffset?.y || 0;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -1,67 +1,115 @@
|
|||
<template>
|
||||
<div class="branch"></div>
|
||||
<div class="branch"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'branch-node',
|
||||
props: {
|
||||
featureType: String,
|
||||
id: [ Number, String ],
|
||||
branches: Array
|
||||
},
|
||||
inject: [ 'registerNode', 'unregisterNode', 'registerBranch', 'unregisterBranch' ],
|
||||
mounted() {
|
||||
const id = `${this.featureType}@${this.id}`;
|
||||
if (this.registerNode) {
|
||||
this.registerNode(id, this);
|
||||
}
|
||||
if (this.registerBranch) {
|
||||
this.branches?.map(this.handleBranch).forEach(branch => this.registerBranch(id, branch));
|
||||
}
|
||||
},
|
||||
beforeUnmount() {
|
||||
const id = `${this.featureType}@${this.id}`;
|
||||
if (this.unregisterNode) {
|
||||
this.unregisterNode(id);
|
||||
}
|
||||
if (this.unregisterBranch) {
|
||||
this.branches?.map(this.handleBranch).forEach(branch => this.unregisterBranch(id, branch));
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
featureType(newValue, oldValue) {
|
||||
if (this.registerNode && this.unregisterNode) {
|
||||
this.unregisterNode(`${oldValue}@${this.id}`);
|
||||
this.registerNode(`${newValue}@${this.id}`, this);
|
||||
}
|
||||
},
|
||||
id(newValue, oldValue) {
|
||||
if (this.registerNode && this.unregisterNode) {
|
||||
this.unregisterNode(`${this.featureType}@${oldValue}`);
|
||||
this.registerNode(`${this.featureType}@${newValue}`, this);
|
||||
}
|
||||
},
|
||||
branches(newValue, oldValue) {
|
||||
if (this.registerBranch && this.unregisterBranch) {
|
||||
const id = `${this.featureType}@${this.id}`;
|
||||
oldValue?.map(this.handleBranch).forEach(branch => this.unregisterBranch(id, branch));
|
||||
newValue?.map(this.handleBranch).forEach(branch => this.registerBranch(id, branch));
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleBranch(branch) {
|
||||
if (typeof branch === 'string') {
|
||||
return branch.includes('@') ? branch : `${this.featureType}@${branch}`;
|
||||
}
|
||||
if (!branch.target?.includes('@')) {
|
||||
return { ...branch, target: `${branch.featureType || this.featureType}@${branch.target}` };
|
||||
}
|
||||
return branch;
|
||||
}
|
||||
}
|
||||
};
|
||||
<script lang="ts">
|
||||
import { BranchOptions } from "@/typings/branches";
|
||||
import { ComponentPublicInstance, defineComponent, PropType } from "vue";
|
||||
|
||||
// Annoying work-around for injected functions not appearing on `this`
|
||||
// Also requires those annoying 3 lines in any function that uses this
|
||||
type BranchInjectedComponent<T extends ComponentPublicInstance> = {
|
||||
registerNode?: (id: string, component: ComponentPublicInstance) => void;
|
||||
unregisterNode?: (id: string) => void;
|
||||
registerBranch?: (start: string, options: string | BranchOptions) => void;
|
||||
unregisterBranch?: (start: string, options: string | BranchOptions) => void;
|
||||
} & T;
|
||||
|
||||
export default defineComponent({
|
||||
name: "branch-node",
|
||||
props: {
|
||||
featureType: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
},
|
||||
branches: Array as PropType<Array<string | BranchOptions>>
|
||||
},
|
||||
inject: ["registerNode", "unregisterNode", "registerBranch", "unregisterBranch"],
|
||||
mounted() {
|
||||
const id = `${this.featureType}@${this.id}`;
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const _this = this;
|
||||
const injectedThis = this as BranchInjectedComponent<typeof _this>;
|
||||
if (injectedThis.registerNode) {
|
||||
injectedThis.registerNode(id, this);
|
||||
}
|
||||
if (injectedThis.registerBranch) {
|
||||
this.branches
|
||||
?.map(this.handleBranch)
|
||||
.forEach(branch => injectedThis.registerBranch!(id, branch));
|
||||
}
|
||||
},
|
||||
beforeUnmount() {
|
||||
const id = `${this.featureType}@${this.id}`;
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const _this = this;
|
||||
const injectedThis = this as BranchInjectedComponent<typeof _this>;
|
||||
if (injectedThis.unregisterNode) {
|
||||
injectedThis.unregisterNode(id);
|
||||
}
|
||||
if (injectedThis.unregisterBranch) {
|
||||
this.branches
|
||||
?.map(this.handleBranch)
|
||||
.forEach(branch => injectedThis.unregisterBranch!(id, branch));
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
featureType(newValue, oldValue) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const _this = this;
|
||||
const injectedThis = this as BranchInjectedComponent<typeof _this>;
|
||||
if (injectedThis.registerNode && injectedThis.unregisterNode) {
|
||||
injectedThis.unregisterNode(`${oldValue}@${this.id}`);
|
||||
injectedThis.registerNode(`${newValue}@${this.id}`, this);
|
||||
}
|
||||
},
|
||||
id(newValue, oldValue) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const _this = this;
|
||||
const injectedThis = this as BranchInjectedComponent<typeof _this>;
|
||||
if (injectedThis.registerNode && injectedThis.unregisterNode) {
|
||||
injectedThis.unregisterNode(`${this.featureType}@${oldValue}`);
|
||||
injectedThis.registerNode(`${this.featureType}@${newValue}`, this);
|
||||
}
|
||||
},
|
||||
branches(newValue, oldValue) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const _this = this;
|
||||
const injectedThis = this as BranchInjectedComponent<typeof _this>;
|
||||
if (injectedThis.registerBranch && injectedThis.unregisterBranch) {
|
||||
const id = `${this.featureType}@${this.id}`;
|
||||
oldValue
|
||||
?.map(this.handleBranch)
|
||||
.forEach((branch: string | BranchOptions) =>
|
||||
injectedThis.unregisterBranch!(id, branch)
|
||||
);
|
||||
newValue
|
||||
?.map(this.handleBranch)
|
||||
.forEach((branch: string | BranchOptions) =>
|
||||
injectedThis.registerBranch!(id, branch)
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleBranch(branch: string | BranchOptions) {
|
||||
if (typeof branch === "string") {
|
||||
return branch.includes("@") ? branch : `${this.featureType}@${branch}`;
|
||||
}
|
||||
if (!branch.target?.includes("@")) {
|
||||
return {
|
||||
...branch,
|
||||
target: `${branch.featureType || this.featureType}@${branch.target}`
|
||||
};
|
||||
}
|
||||
return branch;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -70,8 +118,8 @@ export default {
|
|||
z-index: -10;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,95 +1,118 @@
|
|||
<template>
|
||||
<slot />
|
||||
<div ref="resizeListener" class="resize-listener" />
|
||||
<svg v-bind="$attrs">
|
||||
<branch-line v-for="(branch, index) in branches" :key="index"
|
||||
:startNode="nodes[branch.start]" :endNode="nodes[branch.end]" :options="branch.options" />
|
||||
</svg>
|
||||
<slot />
|
||||
<div ref="resizeListener" class="resize-listener" />
|
||||
<svg v-bind="$attrs">
|
||||
<branch-line
|
||||
v-for="(branch, index) in branches"
|
||||
:key="index"
|
||||
:startNode="nodes[branch.start]"
|
||||
:endNode="nodes[branch.end]"
|
||||
:options="branch.options"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { BranchLink, BranchNode, BranchOptions } from "@/typings/branches";
|
||||
import { ComponentPublicInstance, defineComponent } from "vue";
|
||||
|
||||
const observerOptions = {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
subtree: true
|
||||
attributes: true,
|
||||
childList: true,
|
||||
subtree: true
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'branches',
|
||||
data() {
|
||||
return {
|
||||
observer: new MutationObserver(this.updateNodes),
|
||||
resizeObserver: new ResizeObserver(this.updateNodes),
|
||||
nodes: {},
|
||||
links: []
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// ResizeListener exists because ResizeObserver's don't work when told to observe an SVG element
|
||||
this.resizeObserver.observe(this.$refs.resizeListener);
|
||||
this.updateNodes();
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
registerNode: this.registerNode,
|
||||
unregisterNode: this.unregisterNode,
|
||||
registerBranch: this.registerBranch,
|
||||
unregisterBranch: this.unregisterBranch
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
branches() {
|
||||
return this.links.filter(link => link.start in this.nodes && link.end in this.nodes &&
|
||||
this.nodes[link.start].x != undefined && this.nodes[link.start].y != undefined &&
|
||||
this.nodes[link.end].x != undefined && this.nodes[link.end].y != undefined);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateNodes() {
|
||||
if (this.$refs.resizeListener != undefined) {
|
||||
const containerRect = this.$refs.resizeListener.getBoundingClientRect();
|
||||
Object.keys(this.nodes).forEach(id => this.updateNode(id, containerRect));
|
||||
}
|
||||
},
|
||||
updateNode(id, containerRect) {
|
||||
const linkStartRect = this.nodes[id].element.getBoundingClientRect();
|
||||
this.nodes[id].x = linkStartRect.x + linkStartRect.width / 2 - containerRect.x;
|
||||
this.nodes[id].y = linkStartRect.y + linkStartRect.height / 2 - containerRect.y;
|
||||
},
|
||||
registerNode(id, component) {
|
||||
const element = component.$el.parentElement;
|
||||
this.nodes[id] = { component, element };
|
||||
this.observer.observe(element, observerOptions);
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.resizeListener != undefined) {
|
||||
this.updateNode(id, this.$refs.resizeListener.getBoundingClientRect());
|
||||
}
|
||||
});
|
||||
},
|
||||
unregisterNode(id) {
|
||||
delete this.nodes[id];
|
||||
},
|
||||
registerBranch(start, options) {
|
||||
const end = typeof options === 'string' ? options : options.target;
|
||||
this.links.push({ start, end, options });
|
||||
},
|
||||
unregisterBranch(start, options) {
|
||||
const index = this.links.findIndex(l => l.start === start && l.options === options);
|
||||
this.links.splice(index, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "branches",
|
||||
data() {
|
||||
return {
|
||||
observer: new MutationObserver(this.updateNodes as (...args: unknown[]) => void),
|
||||
resizeObserver: new ResizeObserver(this.updateNodes as (...args: unknown[]) => void),
|
||||
nodes: {},
|
||||
links: []
|
||||
} as {
|
||||
observer: MutationObserver;
|
||||
resizeObserver: ResizeObserver;
|
||||
nodes: Record<string, BranchNode>;
|
||||
links: Array<BranchLink>;
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// ResizeListener exists because ResizeObserver's don't work when told to observe an SVG element
|
||||
this.resizeObserver.observe(this.$refs.resizeListener as HTMLElement);
|
||||
this.updateNodes();
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
registerNode: this.registerNode,
|
||||
unregisterNode: this.unregisterNode,
|
||||
registerBranch: this.registerBranch,
|
||||
unregisterBranch: this.unregisterBranch
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
branches(): Array<BranchLink> {
|
||||
return this.links.filter(
|
||||
link =>
|
||||
link.start in this.nodes &&
|
||||
link.end in this.nodes &&
|
||||
this.nodes[link.start].x != undefined &&
|
||||
this.nodes[link.start].y != undefined &&
|
||||
this.nodes[link.end].x != undefined &&
|
||||
this.nodes[link.end].y != undefined
|
||||
);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateNodes() {
|
||||
if (this.$refs.resizeListener != undefined) {
|
||||
const containerRect = (this.$refs
|
||||
.resizeListener as HTMLElement).getBoundingClientRect();
|
||||
Object.keys(this.nodes).forEach(id => this.updateNode(id, containerRect));
|
||||
}
|
||||
},
|
||||
updateNode(id: string, containerRect: DOMRect) {
|
||||
const linkStartRect = this.nodes[id].element.getBoundingClientRect();
|
||||
this.nodes[id].x = linkStartRect.x + linkStartRect.width / 2 - containerRect.x;
|
||||
this.nodes[id].y = linkStartRect.y + linkStartRect.height / 2 - containerRect.y;
|
||||
},
|
||||
registerNode(id: string, component: ComponentPublicInstance) {
|
||||
const element = component.$el.parentElement;
|
||||
this.nodes[id] = { component, element };
|
||||
this.observer.observe(element, observerOptions);
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.resizeListener != undefined) {
|
||||
this.updateNode(
|
||||
id,
|
||||
(this.$refs.resizeListener as HTMLElement).getBoundingClientRect()
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
unregisterNode(id: string) {
|
||||
delete this.nodes[id];
|
||||
},
|
||||
registerBranch(start: string, options: string | BranchOptions) {
|
||||
const end = typeof options === "string" ? options : options.target;
|
||||
this.links.push({ start, end: end!, options });
|
||||
},
|
||||
unregisterBranch(start: string, options: string | BranchOptions) {
|
||||
const index = this.links.findIndex(l => l.start === start && l.options === options);
|
||||
this.links.splice(index, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
svg,
|
||||
.resize-listener {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -10;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -10;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,75 +1,102 @@
|
|||
<template>
|
||||
<span class="row" v-for="(row, index) in rows" :key="index">
|
||||
<tree-node v-for="(node, nodeIndex) in row" :key="nodeIndex" :id="node" @show-modal="openModal" :append="append" />
|
||||
</span>
|
||||
<span class="side-nodes" v-if="rows.side">
|
||||
<tree-node v-for="(node, nodeIndex) in rows.side" :key="nodeIndex" :id="node" @show-modal="openModal" :append="append" small />
|
||||
</span>
|
||||
<modal :show="showModal" @close="closeModal">
|
||||
<template v-slot:header><h2 v-if="modalHeader">{{ modalHeader }}</h2></template>
|
||||
<template v-slot:body><layer-tab v-if="modal" :layer="modal" :index="tab.index" :forceFirstTab="true" /></template>
|
||||
</modal>
|
||||
<span class="row" v-for="(row, index) in rows" :key="index">
|
||||
<tree-node
|
||||
v-for="(node, nodeIndex) in row"
|
||||
:key="nodeIndex"
|
||||
:id="node"
|
||||
@show-modal="openModal"
|
||||
:append="append"
|
||||
/>
|
||||
</span>
|
||||
<span class="side-nodes" v-if="rows.side">
|
||||
<tree-node
|
||||
v-for="(node, nodeIndex) in rows.side"
|
||||
:key="nodeIndex"
|
||||
:id="node"
|
||||
@show-modal="openModal"
|
||||
:append="append"
|
||||
small
|
||||
/>
|
||||
</span>
|
||||
<modal :show="showModal" @close="closeModal">
|
||||
<template v-slot:header
|
||||
><h2 v-if="modalHeader">{{ modalHeader }}</h2></template
|
||||
>
|
||||
<template v-slot:body
|
||||
><layer-tab v-if="modal" :layer="modal" :index="tab.index" :forceFirstTab="true"
|
||||
/></template>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'tree',
|
||||
data() {
|
||||
return {
|
||||
showModal: false,
|
||||
modal: null
|
||||
};
|
||||
},
|
||||
props: {
|
||||
nodes: Array,
|
||||
append: Boolean
|
||||
},
|
||||
inject: [ 'tab' ],
|
||||
computed: {
|
||||
modalHeader() {
|
||||
if (this.modal == null) {
|
||||
return null;
|
||||
}
|
||||
return layers[this.modal].name;
|
||||
},
|
||||
rows() {
|
||||
if (this.nodes != undefined) {
|
||||
return this.nodes;
|
||||
}
|
||||
const rows = Object.keys(layers).reduce((acc, curr) => {
|
||||
if (!(layers[curr].displayRow in acc)) {
|
||||
acc[layers[curr].displayRow] = [];
|
||||
}
|
||||
if (layers[curr].position != undefined) {
|
||||
acc[layers[curr].displayRow][layers[curr].position] = curr;
|
||||
} else {
|
||||
acc[layers[curr].displayRow].push(curr);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
return Object.keys(rows).reduce((acc, curr) => {
|
||||
acc[curr] = rows[curr].filter(layer => layer);
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openModal(id) {
|
||||
this.showModal = true;
|
||||
this.modal = id;
|
||||
},
|
||||
closeModal() {
|
||||
this.showModal = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "tree",
|
||||
data() {
|
||||
return {
|
||||
showModal: false,
|
||||
modal: null
|
||||
} as {
|
||||
showModal: boolean;
|
||||
modal: string | null;
|
||||
};
|
||||
},
|
||||
props: {
|
||||
nodes: Object as PropType<Record<string, Array<string | number>>>,
|
||||
append: Boolean
|
||||
},
|
||||
inject: ["tab"],
|
||||
computed: {
|
||||
modalHeader(): string | null {
|
||||
if (this.modal == null) {
|
||||
return null;
|
||||
}
|
||||
return layers[this.modal].name || this.modal;
|
||||
},
|
||||
rows(): Record<string | number, Array<string | number>> {
|
||||
if (this.nodes != undefined) {
|
||||
return this.nodes;
|
||||
}
|
||||
const rows = Object.keys(layers).reduce(
|
||||
(acc: Record<string | number, Array<string | number>>, curr) => {
|
||||
if (!(layers[curr].displayRow in acc)) {
|
||||
acc[layers[curr].displayRow] = [];
|
||||
}
|
||||
if (layers[curr].position != undefined) {
|
||||
acc[layers[curr].displayRow][layers[curr].position!] = curr;
|
||||
} else {
|
||||
acc[layers[curr].displayRow].push(curr);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
return Object.keys(rows).reduce(
|
||||
(acc: Record<string | number, Array<string | number>>, curr) => {
|
||||
acc[curr] = rows[curr].filter(layer => layer);
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openModal(id: string) {
|
||||
this.showModal = true;
|
||||
this.modal = id;
|
||||
},
|
||||
closeModal() {
|
||||
this.showModal = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.row {
|
||||
margin: 50px auto;
|
||||
margin: 50px auto;
|
||||
}
|
||||
|
||||
.side-nodes {
|
||||
|
|
|
@ -1,112 +1,130 @@
|
|||
<template>
|
||||
<tooltip :display="tooltip" :force="forceTooltip" :class="{
|
||||
ghost: layer.layerShown === 'ghost',
|
||||
treeNode: true,
|
||||
[id]: true,
|
||||
hidden: !layer.layerShown,
|
||||
locked: !unlocked,
|
||||
notify: layer.notify && unlocked,
|
||||
resetNotify: layer.resetNotify,
|
||||
can: unlocked,
|
||||
small
|
||||
}">
|
||||
<LayerProvider :index="tab.index" :layer="id">
|
||||
<button v-if="layer.shown" @click="clickTab" :style="style" :disabled="!unlocked">
|
||||
<component :is="display" />
|
||||
<branch-node :branches="layer.branches" :id="id" featureType="tree-node" />
|
||||
</button>
|
||||
<mark-node :mark="layer.mark" />
|
||||
</LayerProvider>
|
||||
</tooltip>
|
||||
<tooltip
|
||||
:display="tooltip"
|
||||
:force="forceTooltip"
|
||||
:class="{
|
||||
ghost: layer.layerShown === 'ghost',
|
||||
treeNode: true,
|
||||
[id]: true,
|
||||
hidden: !layer.layerShown,
|
||||
locked: !unlocked,
|
||||
notify: layer.notify && unlocked,
|
||||
resetNotify: layer.resetNotify,
|
||||
can: unlocked,
|
||||
small
|
||||
}"
|
||||
>
|
||||
<LayerProvider :index="tab.index" :layer="id">
|
||||
<button v-if="layer.shown" @click="clickTab" :style="style" :disabled="!unlocked">
|
||||
<component :is="display" />
|
||||
<branch-node :branches="layer.branches" :id="id" featureType="tree-node" />
|
||||
</button>
|
||||
<mark-node :mark="layer.mark" />
|
||||
</LayerProvider>
|
||||
</tooltip>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { layers } from '../../game/layers';
|
||||
import player from '../../game/player';
|
||||
import { coerceComponent } from '../../util/vue';
|
||||
<script lang="ts">
|
||||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { CoercableComponent } from "@/typings/component";
|
||||
import { Layer } from "@/typings/layer";
|
||||
import { coerceComponent } from "@/util/vue";
|
||||
import { Component, defineComponent } from "vue";
|
||||
|
||||
export default {
|
||||
name: 'tree-node',
|
||||
props: {
|
||||
id: [ String, Number ],
|
||||
small: Boolean,
|
||||
append: Boolean
|
||||
},
|
||||
emits: [ 'show-modal' ],
|
||||
inject: [ 'tab' ],
|
||||
computed: {
|
||||
layer() {
|
||||
return layers[this.id];
|
||||
},
|
||||
unlocked() {
|
||||
if (this.layer.canClick != undefined) {
|
||||
return this.layer.canClick;
|
||||
}
|
||||
return this.layer.unlocked;
|
||||
},
|
||||
style() {
|
||||
return [
|
||||
this.unlocked ? { backgroundColor: this.layer.color } : null,
|
||||
this.layer.notify && this.unlocked ?
|
||||
{ boxShadow: `-4px -4px 4px rgba(0, 0, 0, 0.25) inset, 0 0 20px ${this.layer.trueGlowColor}` } : null,
|
||||
this.layer.nodeStyle
|
||||
];
|
||||
},
|
||||
display() {
|
||||
if (this.layer.display != undefined) {
|
||||
return coerceComponent(this.layer.display);
|
||||
} else if (this.layer.image != undefined) {
|
||||
return coerceComponent(`<img src=${this.layer.image}/>`);
|
||||
} else {
|
||||
return coerceComponent(this.layer.symbol);
|
||||
}
|
||||
},
|
||||
forceTooltip() {
|
||||
return player[this.id].forceTooltip;
|
||||
},
|
||||
tooltip() {
|
||||
if (this.layer.canClick != undefined) {
|
||||
if (this.layer.canClick) {
|
||||
return this.layer.tooltip || 'I am a button!';
|
||||
} else {
|
||||
return this.layer.tooltipLocked || this.layer.tooltip || 'I am a button!';
|
||||
}
|
||||
}
|
||||
if (player[this.id].unlocked) {
|
||||
return this.layer.tooltip || `{{ formatWhole(player.${this.id}.points) }} {{ layers.${this.id}.resource }}`;
|
||||
} else {
|
||||
return this.layer.tooltipLocked ||
|
||||
`Reach {{ formatWhole(layers.${this.id}.requires) }} {{ layers.${this.id}.baseResource }} to unlock (You have {{ formatWhole(layers.${this.id}.baseAmount) }} {{ layers.${this.id}.baseResource }})`;
|
||||
}
|
||||
},
|
||||
components() {
|
||||
return Object.keys(layers).reduce((acc, curr) => {
|
||||
acc[curr] = layers[curr].component || false;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clickTab(e) {
|
||||
if (e.shiftKey) {
|
||||
player[this.id].forceTooltip = !player[this.id].forceTooltip;
|
||||
} else if (this.layer.click != undefined) {
|
||||
this.layer.click();
|
||||
} else if (this.layer.modal) {
|
||||
this.$emit('show-modal', this.id);
|
||||
} else if (this.append) {
|
||||
if (player.tabs.includes(this.id)) {
|
||||
const index = player.tabs.lastIndexOf(this.id);
|
||||
player.tabs = [...player.tabs.slice(0, index), ...player.tabs.slice(index + 1)];
|
||||
} else {
|
||||
player.tabs = [...player.tabs, this.id];
|
||||
}
|
||||
} else {
|
||||
player.tabs = [...player.tabs.slice(0, this.tab.index + 1), this.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export default defineComponent({
|
||||
name: "tree-node",
|
||||
props: {
|
||||
id: {
|
||||
type: [String, Number],
|
||||
required: true
|
||||
},
|
||||
small: Boolean,
|
||||
append: Boolean
|
||||
},
|
||||
emits: ["show-modal"],
|
||||
inject: ["tab"],
|
||||
computed: {
|
||||
layer(): Layer {
|
||||
return layers[this.id];
|
||||
},
|
||||
unlocked(): boolean {
|
||||
if (this.layer.canClick != undefined) {
|
||||
return this.layer.canClick;
|
||||
}
|
||||
return this.layer.unlocked;
|
||||
},
|
||||
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||
return [
|
||||
this.unlocked ? { backgroundColor: this.layer.color } : undefined,
|
||||
this.layer.notify && this.unlocked
|
||||
? {
|
||||
boxShadow: `-4px -4px 4px rgba(0, 0, 0, 0.25) inset, 0 0 20px ${this.layer.trueGlowColor}`
|
||||
}
|
||||
: undefined,
|
||||
this.layer.nodeStyle
|
||||
];
|
||||
},
|
||||
display(): Component | string {
|
||||
if (this.layer.display != undefined) {
|
||||
return coerceComponent(this.layer.display);
|
||||
} else if (this.layer.image != undefined) {
|
||||
return coerceComponent(`<img src=${this.layer.image}/>`);
|
||||
} else {
|
||||
return coerceComponent(this.layer.symbol);
|
||||
}
|
||||
},
|
||||
forceTooltip(): boolean {
|
||||
return player.layers[this.id].forceTooltip === true;
|
||||
},
|
||||
tooltip(): CoercableComponent {
|
||||
if (this.layer.canClick != undefined) {
|
||||
if (this.layer.canClick) {
|
||||
return this.layer.tooltip || "I am a button!";
|
||||
} else {
|
||||
return this.layer.tooltipLocked || this.layer.tooltip || "I am a button!";
|
||||
}
|
||||
}
|
||||
if (player.layers[this.id].unlocked) {
|
||||
return (
|
||||
this.layer.tooltip ||
|
||||
`{{ formatWhole(player.${this.id}.points) }} {{ layers.${this.id}.resource }}`
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
this.layer.tooltipLocked ||
|
||||
`Reach {{ formatWhole(layers.${this.id}.requires) }} {{ layers.${this.id}.baseResource }} to unlock (You have {{ formatWhole(layers.${this.id}.baseAmount) }} {{ layers.${this.id}.baseResource }})`
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clickTab(e: MouseEvent) {
|
||||
if (e.shiftKey) {
|
||||
player.layers[this.id].forceTooltip = !player.layers[this.id].forceTooltip;
|
||||
} else if (this.layer.click != undefined) {
|
||||
this.layer.click();
|
||||
} else if (this.layer.modal) {
|
||||
this.$emit("show-modal", this.id);
|
||||
} else if (this.append) {
|
||||
if (player.tabs.includes(this.id.toString())) {
|
||||
const index = player.tabs.lastIndexOf(this.id.toString());
|
||||
player.tabs = [...player.tabs.slice(0, index), ...player.tabs.slice(index + 1)];
|
||||
} else {
|
||||
player.tabs = [...player.tabs, this.id.toString()];
|
||||
}
|
||||
} else {
|
||||
player.tabs = [
|
||||
...player.tabs.slice(
|
||||
0,
|
||||
((this as unknown) as { tab: { index: number } }).tab.index + 1
|
||||
),
|
||||
this.id.toString()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -119,8 +137,8 @@ export default {
|
|||
}
|
||||
|
||||
.treeNode button {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 2px solid rgba(0, 0, 0, 0.125);
|
||||
border-radius: inherit;
|
||||
font-size: 40px;
|
||||
|
@ -140,7 +158,7 @@ export default {
|
|||
}
|
||||
|
||||
.ghost {
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
import Decimal from '../../../util/bignum';
|
||||
import player from '../../../game/player';
|
||||
|
||||
export default {
|
||||
id: "a",
|
||||
startData() { return {
|
||||
unlocked: true,
|
||||
points: new Decimal(0),
|
||||
}},
|
||||
color: "yellow",
|
||||
modal: true,
|
||||
name: "Achievements",
|
||||
resource: "achievement power",
|
||||
row: "side",
|
||||
tooltip() { // Optional, tooltip displays when the layer is locked
|
||||
return ("Achievements")
|
||||
},
|
||||
achievementPopups: true,
|
||||
achievements: {
|
||||
11: {
|
||||
image: "https://unsoftcapped2.github.io/The-Modding-Tree-2/discord.png",
|
||||
name: "Get me!",
|
||||
done() {return true}, // This one is a freebie
|
||||
goalTooltip: "How did this happen?", // Shows when achievement is not completed
|
||||
doneTooltip: "You did it!", // Showed when the achievement is completed
|
||||
},
|
||||
12: {
|
||||
name: "Impossible!",
|
||||
done() {return false},
|
||||
goalTooltip: "Mwahahaha!", // Shows when achievement is not completed
|
||||
doneTooltip: "HOW????", // Showed when the achievement is completed
|
||||
style: {'color': '#04e050'},
|
||||
},
|
||||
13: {
|
||||
name: "EIEIO",
|
||||
done() {return player.f.points.gte(1)},
|
||||
tooltip: "Get a farm point.\n\nReward: The dinosaur is now your friend (you can max Farm Points).", // Showed when the achievement is completed
|
||||
onComplete() {console.log("Bork bork bork!")}
|
||||
},
|
||||
},
|
||||
midsection: "<grid id='test' />",
|
||||
grids: {
|
||||
test: {
|
||||
maxRows: 3,
|
||||
rows: 2,
|
||||
cols: 2,
|
||||
getStartData(cell) {
|
||||
return cell
|
||||
},
|
||||
getUnlocked() { // Default
|
||||
return true
|
||||
},
|
||||
getCanClick() {
|
||||
return player.points.eq(10)
|
||||
},
|
||||
getStyle(cell, data) {
|
||||
return {'background-color': '#'+ (data*1234%999999)}
|
||||
},
|
||||
click() { // Don't forget onHold
|
||||
this.data++
|
||||
},
|
||||
getTitle(cell) {
|
||||
let direction;
|
||||
if (cell === '101') {
|
||||
direction = 'top';
|
||||
} else if (cell === '102') {
|
||||
direction = 'bottom';
|
||||
} else if (cell === '201') {
|
||||
direction = 'left';
|
||||
} else if (cell === '202') {
|
||||
direction = 'right';
|
||||
}
|
||||
return `<tooltip display='${JSON.stringify(this.style)}' ${direction}>
|
||||
<h3>Gridable #${cell}</h3>
|
||||
</tooltip>`
|
||||
},
|
||||
getDisplay(cell, data) {
|
||||
return data
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
103
src/data/layers/aca/a.ts
Normal file
103
src/data/layers/aca/a.ts
Normal file
|
@ -0,0 +1,103 @@
|
|||
/* eslint-disable */
|
||||
import player from "@/game/player";
|
||||
import { GridCell } from "@/typings/features/grid";
|
||||
import { RawLayer } from "@/typings/layer";
|
||||
import Decimal from "@/util/bignum";
|
||||
|
||||
export default {
|
||||
id: "a",
|
||||
startData() {
|
||||
return {
|
||||
unlocked: true,
|
||||
points: new Decimal(0)
|
||||
};
|
||||
},
|
||||
color: "yellow",
|
||||
modal: true,
|
||||
name: "Achievements",
|
||||
resource: "achievement power",
|
||||
row: "side",
|
||||
tooltip() {
|
||||
// Optional, tooltip displays when the layer is locked
|
||||
return "Achievements";
|
||||
},
|
||||
achievementPopups: true,
|
||||
achievements: {
|
||||
data: {
|
||||
11: {
|
||||
image: "https://unsoftcapped2.github.io/The-Modding-Tree-2/discord.png",
|
||||
name: "Get me!",
|
||||
done() {
|
||||
return true;
|
||||
}, // This one is a freebie
|
||||
goalTooltip: "How did this happen?", // Shows when achievement is not completed
|
||||
doneTooltip: "You did it!" // Showed when the achievement is completed
|
||||
},
|
||||
12: {
|
||||
name: "Impossible!",
|
||||
done() {
|
||||
return false;
|
||||
},
|
||||
goalTooltip: "Mwahahaha!", // Shows when achievement is not completed
|
||||
doneTooltip: "HOW????", // Showed when the achievement is completed
|
||||
style: { color: "#04e050" }
|
||||
},
|
||||
13: {
|
||||
name: "EIEIO",
|
||||
done() {
|
||||
return player.layers.f.points.gte(1);
|
||||
},
|
||||
tooltip:
|
||||
"Get a farm point.\n\nReward: The dinosaur is now your friend (you can max Farm Points).", // Showed when the achievement is completed
|
||||
onComplete() {
|
||||
console.log("Bork bork bork!");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
midsection: "<grid id='test' />",
|
||||
grids: {
|
||||
data: {
|
||||
test: {
|
||||
maxRows: 3,
|
||||
rows: 2,
|
||||
cols: 2,
|
||||
getStartData(cell: string) {
|
||||
return cell;
|
||||
},
|
||||
getUnlocked() {
|
||||
// Default
|
||||
return true;
|
||||
},
|
||||
getCanClick() {
|
||||
return player.points.eq(10);
|
||||
},
|
||||
getStyle(cell) {
|
||||
return { backgroundColor: "#" + ((Number((this[cell] as GridCell).data) * 1234) % 999999) };
|
||||
},
|
||||
click(cell) {
|
||||
// Don't forget onHold
|
||||
(this[cell] as GridCell).data = ((this[cell] as GridCell).data as number) + 1;
|
||||
},
|
||||
getTitle(cell) {
|
||||
let direction;
|
||||
if (cell === "101") {
|
||||
direction = "top";
|
||||
} else if (cell === "102") {
|
||||
direction = "bottom";
|
||||
} else if (cell === "201") {
|
||||
direction = "left";
|
||||
} else if (cell === "202") {
|
||||
direction = "right";
|
||||
}
|
||||
return `<tooltip display='${JSON.stringify(this.style)}' ${direction}>
|
||||
<h3>Gridable #${cell}</h3>
|
||||
</tooltip>`;
|
||||
},
|
||||
getDisplay(cell) {
|
||||
return (this[cell] as GridCell).data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} as RawLayer;
|
|
@ -1,399 +0,0 @@
|
|||
import Decimal, { format, formatWhole } from '../../../util/bignum';
|
||||
import player from '../../../game/player';
|
||||
import { layers } from '../../../game/layers';
|
||||
import { hasUpgrade, hasMilestone, getBuyableAmount, setBuyableAmount, upgradeEffect, buyableEffect, challengeCompletions } from '../../../util/features';
|
||||
import { resetLayer, resetLayerData } from '../../../util/layers';
|
||||
import { UP, RIGHT } from '../../../util/vue';
|
||||
|
||||
const tmp = layers;
|
||||
|
||||
export default {
|
||||
id: "c", // This is assigned automatically, both to the layer and all upgrades, etc. Shown here so you know about it
|
||||
name: "Candies", // This is optional, only used in a few places, If absent it just uses the layer id.
|
||||
symbol: "C", // This appears on the layer's node. Default is the id with the first letter capitalized
|
||||
position: 0, // Horizontal position within a row. By default it uses the layer id and sorts in alphabetical order
|
||||
startData() { return {
|
||||
unlocked: true,
|
||||
points: new Decimal(0),
|
||||
best: new Decimal(0),
|
||||
total: new Decimal(0),
|
||||
beep: false,
|
||||
thingy: "pointy",
|
||||
otherThingy: 10,
|
||||
spentOnBuyables: new Decimal(0)
|
||||
}},
|
||||
minWidth: 800,
|
||||
color: "#4BDC13",
|
||||
requires: new Decimal(10), // Can be a function that takes requirement increases into account
|
||||
resource: "lollipops", // Name of prestige currency
|
||||
baseResource: "points", // Name of resource prestige is based on
|
||||
baseAmount() {return player.points}, // Get the current amount of baseResource
|
||||
type: "normal", // normal: cost to gain currency depends on amount gained. static: cost depends on how much you already have
|
||||
exponent: 0.5, // Prestige currency exponent
|
||||
base: 5, // Only needed for static layers, base of the formula (b^(x^exp))
|
||||
roundUpCost: false, // True if the cost needs to be rounded up (use when baseResource is static?)
|
||||
|
||||
// For normal layers, gain beyond [softcap] points is put to the [softcapPower]th power
|
||||
softcap: new Decimal(1e100),
|
||||
softcapPower: new Decimal(0.5),
|
||||
canBuyMax() {}, // Only needed for static layers with buy max
|
||||
gainMult() { // Calculate the multiplier for main currency from bonuses
|
||||
let mult = new Decimal(1)
|
||||
if (hasUpgrade(this.layer, 166)) mult = mult.times(2) // These upgrades don't exist
|
||||
if (hasUpgrade(this.layer, 120)) mult = mult.times(upgradeEffect(this.layer, 120))
|
||||
return mult
|
||||
},
|
||||
gainExp() { // Calculate the exponent on main currency from bonuses
|
||||
return new Decimal(1)
|
||||
},
|
||||
row: 0, // Row the layer is in on the tree (0 is the first row)
|
||||
effect() {
|
||||
return { // Formulas for any boosts inherent to resources in the layer. Can return a single value instead of an object if there is just one effect
|
||||
waffleBoost: Decimal.pow(player[this.layer].points, 0.2),
|
||||
icecreamCap: (player[this.layer].points * 10)
|
||||
}},
|
||||
effectDisplay() { // Optional text to describe the effects
|
||||
let eff = this.effect;
|
||||
const waffleBoost = eff.waffleBoost.times(buyableEffect(this.layer, 11).first)
|
||||
return "which are boosting waffles by "+format(waffleBoost)+" and increasing the Ice Cream cap by "+format(eff.icecreamCap)
|
||||
},
|
||||
infoboxes:{
|
||||
coolInfo: {
|
||||
title: "Lore",
|
||||
titleStyle: {'color': '#FE0000'},
|
||||
body: "DEEP LORE!",
|
||||
bodyStyle: {'background-color': "#0000EE"}
|
||||
}
|
||||
},
|
||||
milestones: {
|
||||
0: {requirementDisplay: "3 Lollipops",
|
||||
done() {return player[this.layer].best.gte(3)}, // Used to determine when to give the milestone
|
||||
effectDisplay: "Unlock the next milestone",
|
||||
},
|
||||
1: {requirementDisplay: "4 Lollipops",
|
||||
unlocked() {return hasMilestone(this.layer, 0)},
|
||||
done() {return player[this.layer].best.gte(4)},
|
||||
effectDisplay: "You can toggle beep and boop (which do nothing)",
|
||||
optionsDisplay: `
|
||||
<div style="display: flex; justify-content: center">
|
||||
<Toggle :value="player.c.beep" @change="value => player.c.beep = value" />
|
||||
<Toggle :value="player.f.boop" @change="value => player.f.boop = value" />
|
||||
</div>
|
||||
`,
|
||||
style() {
|
||||
if(hasMilestone(this.layer, this.id)) return {
|
||||
'background-color': '#1111DD'
|
||||
}},
|
||||
|
||||
},
|
||||
},
|
||||
challenges: {
|
||||
|
||||
11: {
|
||||
name: "Fun",
|
||||
completionLimit: 3,
|
||||
challengeDescription() {return "Makes the game 0% harder<br>"+challengeCompletions(this.layer, this.id) + "/" + this.completionLimit + " completions"},
|
||||
unlocked() { return player[this.layer].best.gt(0) },
|
||||
goalDescription: 'Have 20 points I guess',
|
||||
canComplete() {
|
||||
return player.points.gte(20)
|
||||
},
|
||||
rewardEffect() {
|
||||
let ret = player[this.layer].points.add(1).tetrate(0.02)
|
||||
return ret;
|
||||
},
|
||||
rewardDisplay() { return format(this.rewardEffect)+"x" },
|
||||
countsAs: [12, 21], // Use this for if a challenge includes the effects of other challenges. Being in this challenge "counts as" being in these.
|
||||
rewardDescription: "Says hi",
|
||||
onComplete() {console.log("hiii")}, // Called when you successfully complete the challenge
|
||||
onEnter() {console.log("So challenging")},
|
||||
onExit() {console.log("Sweet freedom!")},
|
||||
|
||||
},
|
||||
},
|
||||
upgrades: {
|
||||
|
||||
11: {
|
||||
title: "Generator of Genericness",
|
||||
description: "Gain 1 Point every second.",
|
||||
cost: new Decimal(1),
|
||||
unlocked() { return player[this.layer].unlocked }, // The upgrade is only visible when this is true
|
||||
},
|
||||
12: {
|
||||
description: "Point generation is faster based on your unspent Lollipops.",
|
||||
cost: new Decimal(1),
|
||||
unlocked() { return (hasUpgrade(this.layer, 11))},
|
||||
effect() { // Calculate bonuses from the upgrade. Can return a single value or an object with multiple values
|
||||
let ret = player[this.layer].points.add(1).pow(player[this.layer].upgrades.includes(24)?1.1:(player[this.layer].upgrades.includes(14)?0.75:0.5))
|
||||
if (ret.gte("1e20000000")) ret = ret.sqrt().times("1e10000000")
|
||||
return ret;
|
||||
},
|
||||
effectDisplay() { return format(this.effect)+"x" }, // Add formatting to the effect
|
||||
},
|
||||
13: {
|
||||
unlocked() { return (hasUpgrade(this.layer, 12))},
|
||||
onPurchase() { // This function triggers when the upgrade is purchased
|
||||
player[this.layer].unlockOrder = 0
|
||||
},
|
||||
style() {
|
||||
if (hasUpgrade(this.layer, this.id)) return {
|
||||
'background-color': '#1111dd'
|
||||
}
|
||||
else if (!this.canAfford) {
|
||||
return {
|
||||
'background-color': '#dd1111'
|
||||
}
|
||||
} // Otherwise use the default
|
||||
},
|
||||
canAfford(){return player.points.lte(7)},
|
||||
pay(){player.points = player.points.add(7)},
|
||||
fullDisplay: "Only buyable with less than 7 points, and gives you 7 more. Unlocks a secret subtab."
|
||||
},
|
||||
22: {
|
||||
title: "This upgrade doesn't exist",
|
||||
description: "Or does it?.",
|
||||
currencyLocation() {return player[this.layer].buyables}, // The object in player data that the currency is contained in
|
||||
currencyDisplayName: "exhancers", // Use if using a nonstandard currency
|
||||
currencyInternalName: 11, // Use if using a nonstandard currency
|
||||
|
||||
cost: new Decimal(3),
|
||||
unlocked() { return player[this.layer].unlocked }, // The upgrade is only visible when this is true
|
||||
},
|
||||
},
|
||||
buyables: {
|
||||
showRespec: true,
|
||||
respec() { // Optional, reset things and give back your currency. Having this function makes a respec button appear
|
||||
player[this.layer].points = player[this.layer].points.add(player[this.layer].spentOnBuyables) // A built-in thing to keep track of this but only keeps a single value
|
||||
this.reset();
|
||||
resetLayer(this.layer, true) // Force a reset
|
||||
},
|
||||
respecText: "Respec Thingies", // Text on Respec button, optional
|
||||
respecMessage: "Are you sure? Respeccing these doesn't accomplish much.",
|
||||
11: {
|
||||
title: "Exhancers", // Optional, displayed at the top in a larger font
|
||||
cost() { // cost for buying xth buyable, can be an object if there are multiple currencies
|
||||
let x = this.amount;
|
||||
if (x.gte(25)) x = x.pow(2).div(25)
|
||||
let cost = Decimal.pow(2, x.pow(1.5))
|
||||
return cost.floor()
|
||||
},
|
||||
effect() { // Effects of owning x of the items, x is a decimal
|
||||
let x = this.amount;
|
||||
let eff = {}
|
||||
if (x.gte(0)) eff.first = Decimal.pow(25, x.pow(1.1))
|
||||
else eff.first = Decimal.pow(1/25, x.times(-1).pow(1.1))
|
||||
|
||||
if (x.gte(0)) eff.second = x.pow(0.8)
|
||||
else eff.second = x.times(-1).pow(0.8).times(-1)
|
||||
return eff;
|
||||
},
|
||||
display() { // Everything else displayed in the buyable button after the title
|
||||
let data = tmp[this.layer].buyables[this.id]
|
||||
return "Cost: " + format(data.cost) + " lollipops\n\
|
||||
Amount: " + player[this.layer].buyables[this.id] + "/4\n\
|
||||
Adds + " + format(data.effect.first) + " things and multiplies stuff by " + format(data.effect.second)
|
||||
},
|
||||
unlocked() { return player[this.layer].unlocked },
|
||||
canAfford() {
|
||||
return player[this.layer].points.gte(tmp[this.layer].buyables[this.id].cost)},
|
||||
buy() {
|
||||
let cost = tmp[this.layer].buyables[this.id].cost
|
||||
player[this.layer].points = player[this.layer].points.sub(cost)
|
||||
player[this.layer].buyables[this.id] = player[this.layer].buyables[this.id].add(1)
|
||||
player[this.layer].spentOnBuyables = player[this.layer].spentOnBuyables.add(cost) // This is a built-in system that you can use for respeccing but it only works with a single Decimal value
|
||||
},
|
||||
buyMax() {}, // You'll have to handle this yourself if you want
|
||||
style: {'height':'222px'},
|
||||
purchaseLimit: new Decimal(4),
|
||||
sellOne() {
|
||||
let amount = getBuyableAmount(this.layer, this.id)
|
||||
if (amount.lte(0)) return // Only sell one if there is at least one
|
||||
setBuyableAmount(this.layer, this.id, amount.sub(1))
|
||||
player[this.layer].points = player[this.layer].points.add(this.cost)
|
||||
},
|
||||
},
|
||||
},
|
||||
doReset(resettingLayer){ // Triggers when this layer is being reset, along with the layer doing the resetting. Not triggered by lower layers resetting, but is by layers on the same row.
|
||||
if(layers[resettingLayer].row > this.row) resetLayerData(this.layer, ["points"]) // This is actually the default behavior
|
||||
},
|
||||
automate() {
|
||||
}, // Do any automation inherent to this layer if appropriate
|
||||
resetsNothing() {return false},
|
||||
onPrestige() {
|
||||
return
|
||||
}, // Useful for if you gain secondary resources or have other interesting things happen to this layer when you reset it. You gain the currency after this function ends.
|
||||
|
||||
hotkeys: [
|
||||
{key: "c", description: "reset for lollipops or whatever", press(){if (layers[this.layer].canReset) resetLayer(this.layer)}},
|
||||
{key: "ctrl+c", description: "respec things", press(){layers[this.layer].buyables.respec()}, unlocked() {return hasUpgrade('c', '22')}} ,
|
||||
],
|
||||
increaseUnlockOrder: [], // Array of layer names to have their order increased when this one is first unlocked
|
||||
|
||||
microtabs: {
|
||||
stuff: {
|
||||
first: {
|
||||
display: `
|
||||
<upgrades />
|
||||
<div>confirmed</div>`
|
||||
},
|
||||
second: {
|
||||
embedLayer: "f"
|
||||
},
|
||||
},
|
||||
otherStuff: {
|
||||
// There could be another set of microtabs here
|
||||
}
|
||||
},
|
||||
|
||||
bars: {
|
||||
longBoi: {
|
||||
fillStyle: {'background-color' : "#FFFFFF"},
|
||||
baseStyle: {'background-color' : "#696969"},
|
||||
textStyle: {'color': '#04e050'},
|
||||
|
||||
borderStyle() {return {}},
|
||||
direction: RIGHT,
|
||||
width: 300,
|
||||
height: 30,
|
||||
progress() {
|
||||
return (player.points.add(1).log(10).div(10)).toNumber()
|
||||
},
|
||||
display() {
|
||||
return format(player.points) + " / 1e10 points"
|
||||
},
|
||||
unlocked: true,
|
||||
|
||||
},
|
||||
tallBoi: {
|
||||
fillStyle: {'background-color' : "#4BEC13"},
|
||||
baseStyle: {'background-color' : "#000000"},
|
||||
textStyle: {'text-shadow': '0px 0px 2px #000000'},
|
||||
|
||||
borderStyle() {return {'border-width': "7px"}},
|
||||
direction: UP,
|
||||
width: 50,
|
||||
height: 200,
|
||||
progress() {
|
||||
return player.points.div(100)
|
||||
},
|
||||
display() {
|
||||
return formatWhole((player.points.div(1)).min(100)) + "%"
|
||||
},
|
||||
unlocked: true,
|
||||
|
||||
},
|
||||
flatBoi: {
|
||||
fillStyle: {'background-color' : "#FE0102"},
|
||||
baseStyle: {'background-color' : "#222222"},
|
||||
textStyle: {'text-shadow': '0px 0px 2px #000000'},
|
||||
|
||||
borderStyle() {return {}},
|
||||
direction: UP,
|
||||
width: 100,
|
||||
height: 30,
|
||||
progress() {
|
||||
return player.c.points.div(50)
|
||||
},
|
||||
unlocked: true,
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
// Optional, lets you format the tab yourself by listing components. You can create your own components in v.js.
|
||||
subtabs: {
|
||||
"main tab": {
|
||||
buttonStyle() {return {'color': 'orange'}},
|
||||
notify: true,
|
||||
display: `
|
||||
<main-display />
|
||||
<sticky><prestige-button /></sticky>
|
||||
<resource-display />
|
||||
<spacer height="5px" />
|
||||
<button onclick='console.log("yeet")'>'HI'</button>
|
||||
<div>Name your points!</div>
|
||||
<TextField :value="player.c.thingy" @input="value => player.c.thingy = value" :field="false" />
|
||||
<sticky style="color: red; font-size: 32px; font-family: Comic Sans MS;">I have {{ format(player.points) }} {{ player.c.thingy }} points!</sticky>
|
||||
<hr />
|
||||
<milestones />
|
||||
<spacer />
|
||||
<upgrades />
|
||||
<challenges />`,
|
||||
glowColor: "blue",
|
||||
|
||||
},
|
||||
thingies: {
|
||||
resetNotify: true,
|
||||
style() {return {'background-color': '#222222', '--background': '#222222'}},
|
||||
buttonStyle() {return {'border-color': 'orange'}},
|
||||
display: `
|
||||
<buyables />
|
||||
<spacer />
|
||||
<row style="width: 600px; height: 350px; background-color: green; border-style: solid;">
|
||||
<Toggle :value="player.c.beep" @change="value => player.c.beep = value" />
|
||||
<spacer width="30px" height="10px" />
|
||||
<div>Beep</div>
|
||||
<spacer />
|
||||
<vr height="200px"/>
|
||||
<column>
|
||||
<prestige-button style="width: 150px; height: 80px" />
|
||||
<prestige-button style="width: 100px; height: 150px" />
|
||||
</column>
|
||||
</row>
|
||||
<spacer />
|
||||
<img src="https://unsoftcapped2.github.io/The-Modding-Tree-2/discord.png" />`
|
||||
},
|
||||
jail: {
|
||||
display: `
|
||||
<infobox id="coolInfo" />
|
||||
<bar id="longBoi" />
|
||||
<spacer />
|
||||
<row>
|
||||
<column style="background-color: #555555; padding: 15px">
|
||||
<div style="color: teal">Sugar level:</div><spacer /><bar id="tallBoi" />
|
||||
</column>
|
||||
<spacer />
|
||||
<column>
|
||||
<div>idk</div>
|
||||
<spacer width="0" height="50px" />
|
||||
<bar id="flatBoi" />
|
||||
</column>
|
||||
</row>
|
||||
<spacer />
|
||||
<div>It's jail because "bars"! So funny! Ha ha!</div>
|
||||
<tree :nodes="[['f', 'c'], ['g', 'spook', 'h']]" />`
|
||||
},
|
||||
illuminati: {
|
||||
unlocked() {return (hasUpgrade("c", 13))},
|
||||
display: `
|
||||
<h1> C O N F I R M E D </h1>
|
||||
<spacer />
|
||||
<microtab family="stuff" style="width: 660px; height: 370px; background-color: brown; --background: brown; border: solid white; margin: auto" />
|
||||
<div>Adjust how many points H gives you!</div>
|
||||
<Slider :value="player.c.otherThingy" @change="value => player.c.otherThingy = value" :min="1" :max="30" />`
|
||||
}
|
||||
|
||||
},
|
||||
style() {return {
|
||||
//'background-color': '#3325CC'
|
||||
}},
|
||||
nodeStyle() {return { // Style on the layer node
|
||||
'color': '#3325CC',
|
||||
'text-decoration': 'underline'
|
||||
}},
|
||||
glowColor: "orange", // If the node is highlighted, it will be this color (default is red)
|
||||
componentStyles: {
|
||||
"challenge"() {return {'height': '200px'}},
|
||||
"prestige-button"() {return {'color': '#AA66AA'}},
|
||||
},
|
||||
tooltip() { // Optional, tooltip displays when the layer is unlocked
|
||||
let tooltip = "{{ formatWhole(player.c.points) }} {{ layers.c.resource }}";
|
||||
if (player[this.layer].buyables[11].gt(0)) tooltip += "<br><i><br><br><br>{{ formatWhole(player.c.buyables[11]) }} Exhancers</i>"
|
||||
return tooltip
|
||||
},
|
||||
shouldNotify() { // Optional, layer will be highlighted on the tree if true.
|
||||
// Layer will automatically highlight if an upgrade is purchasable.
|
||||
return (player.c.buyables[11] == 1)
|
||||
},
|
||||
mark: "https://unsoftcapped2.github.io/The-Modding-Tree-2/discord.png",
|
||||
resetDescription: "Melt your points into ",
|
||||
};
|
573
src/data/layers/aca/c.ts
Normal file
573
src/data/layers/aca/c.ts
Normal file
|
@ -0,0 +1,573 @@
|
|||
/* eslint-disable */
|
||||
import { Direction } from "@/game/enums";
|
||||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { DecimalSource } from "@/lib/break_eternity";
|
||||
import { RawLayer } from "@/typings/layer";
|
||||
import Decimal, { format, formatWhole } from "@/util/bignum";
|
||||
import {
|
||||
buyableEffect,
|
||||
challengeCompletions,
|
||||
getBuyableAmount,
|
||||
hasMilestone,
|
||||
hasUpgrade,
|
||||
setBuyableAmount,
|
||||
upgradeEffect
|
||||
} from "@/util/features";
|
||||
import { resetLayer, resetLayerData } from "@/util/layers";
|
||||
|
||||
export default {
|
||||
id: "c", // This is assigned automatically, both to the layer and all upgrades, etc. Shown here so you know about it
|
||||
name: "Candies", // This is optional, only used in a few places, If absent it just uses the layer id.
|
||||
symbol: "C", // This appears on the layer's node. Default is the id with the first letter capitalized
|
||||
position: 0, // Horizontal position within a row. By default it uses the layer id and sorts in alphabetical order
|
||||
startData() {
|
||||
return {
|
||||
unlocked: true,
|
||||
points: new Decimal(0),
|
||||
best: new Decimal(0),
|
||||
total: new Decimal(0),
|
||||
beep: false,
|
||||
thingy: "pointy",
|
||||
otherThingy: 10,
|
||||
spentOnBuyables: new Decimal(0)
|
||||
};
|
||||
},
|
||||
minWidth: 800,
|
||||
color: "#4BDC13",
|
||||
requires: new Decimal(10), // Can be a function that takes requirement increases into account
|
||||
resource: "lollipops", // Name of prestige currency
|
||||
baseResource: "points", // Name of resource prestige is based on
|
||||
baseAmount() {
|
||||
return player.points;
|
||||
}, // Get the current amount of baseResource
|
||||
type: "normal", // normal: cost to gain currency depends on amount gained. static: cost depends on how much you already have
|
||||
exponent: 0.5, // Prestige currency exponent
|
||||
base: 5, // Only needed for static layers, base of the formula (b^(x^exp))
|
||||
roundUpCost: false, // True if the cost needs to be rounded up (use when baseResource is static?)
|
||||
|
||||
// For normal layers, gain beyond [softcap] points is put to the [softcapPower]th power
|
||||
softcap: new Decimal(1e100),
|
||||
softcapPower: new Decimal(0.5),
|
||||
canBuyMax() {}, // Only needed for static layers with buy max
|
||||
gainMult() {
|
||||
// Calculate the multiplier for main currency from bonuses
|
||||
let mult = new Decimal(1);
|
||||
/*
|
||||
if (hasUpgrade(this.layer, 166)) mult = mult.times(2); // These upgrades don't exist
|
||||
if (hasUpgrade(this.layer, 120))
|
||||
mult = mult.times(upgradeEffect(this.layer, 120) as DecimalSource);
|
||||
*/
|
||||
return mult;
|
||||
},
|
||||
gainExp() {
|
||||
// Calculate the exponent on main currency from bonuses
|
||||
return new Decimal(1);
|
||||
},
|
||||
row: 0, // Row the layer is in on the tree (0 is the first row)
|
||||
effect() {
|
||||
return {
|
||||
// Formulas for any boosts inherent to resources in the layer. Can return a single value instead of an object if there is just one effect
|
||||
waffleBoost: Decimal.pow(player.layers[this.layer].points, 0.2),
|
||||
icecreamCap: player.layers[this.layer].points.times(10)
|
||||
};
|
||||
},
|
||||
effectDisplay() {
|
||||
// Optional text to describe the effects
|
||||
const eff = this.effect as { waffleBoost: Decimal; icecreamCap: Decimal };
|
||||
const waffleBoost = eff.waffleBoost.times(
|
||||
(buyableEffect(this.layer, 11) as { first: Decimal, second: Decimal }).first
|
||||
);
|
||||
return (
|
||||
"which are boosting waffles by " +
|
||||
format(waffleBoost) +
|
||||
" and increasing the Ice Cream cap by " +
|
||||
format(eff.icecreamCap)
|
||||
);
|
||||
},
|
||||
infoboxes: {
|
||||
data: {
|
||||
coolInfo: {
|
||||
title: "Lore",
|
||||
titleStyle: { color: "#FE0000" },
|
||||
body: "DEEP LORE!",
|
||||
bodyStyle: { "background-color": "#0000EE" }
|
||||
}
|
||||
}
|
||||
},
|
||||
milestones: {
|
||||
data: {
|
||||
0: {
|
||||
requirementDisplay: "3 Lollipops",
|
||||
done() {
|
||||
return (player.layers[this.layer].best as Decimal).gte(3);
|
||||
}, // Used to determine when to give the milestone
|
||||
effectDisplay: "Unlock the next milestone"
|
||||
},
|
||||
1: {
|
||||
requirementDisplay: "4 Lollipops",
|
||||
unlocked() {
|
||||
return hasMilestone(this.layer, 0);
|
||||
},
|
||||
done() {
|
||||
return (player.layers[this.layer].best as Decimal).gte(4);
|
||||
},
|
||||
effectDisplay: "You can toggle beep and boop (which do nothing)",
|
||||
optionsDisplay: `
|
||||
<div style="display: flex; justify-content: center">
|
||||
<Toggle :value="player.layers.c.beep" @change="value => player.layers.c.beep = value" />
|
||||
<Toggle :value="player.layers.f.boop" @change="value => player.layers.f.boop = value" />
|
||||
</div>
|
||||
`,
|
||||
style() {
|
||||
if (hasMilestone(this.layer, this.id))
|
||||
return {
|
||||
backgroundColor: "#1111DD"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
challenges: {
|
||||
data: {
|
||||
11: {
|
||||
name: "Fun",
|
||||
completionLimit: 3,
|
||||
challengeDescription() {
|
||||
return (
|
||||
"Makes the game 0% harder<br>" +
|
||||
challengeCompletions(this.layer, this.id) +
|
||||
"/" +
|
||||
this.completionLimit +
|
||||
" completions"
|
||||
);
|
||||
},
|
||||
unlocked() {
|
||||
return (player.layers[this.layer].best as Decimal).gt(0);
|
||||
},
|
||||
goalDescription: "Have 20 points I guess",
|
||||
canComplete() {
|
||||
return player.points.gte(20);
|
||||
},
|
||||
effect() {
|
||||
const ret = player.layers[this.layer].points.add(1).tetrate(0.02);
|
||||
return ret;
|
||||
},
|
||||
rewardDisplay() {
|
||||
return format(this.effect as Decimal) + "x";
|
||||
},
|
||||
countsAs: [12, 21], // Use this for if a challenge includes the effects of other challenges. Being in this challenge "counts as" being in these.
|
||||
rewardDescription: "Says hi",
|
||||
onComplete() {
|
||||
console.log("hiii");
|
||||
}, // Called when you successfully complete the challenge
|
||||
onEnter() {
|
||||
console.log("So challenging");
|
||||
},
|
||||
onExit() {
|
||||
console.log("Sweet freedom!");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
upgrades: {
|
||||
data: {
|
||||
11: {
|
||||
title: "Generator of Genericness",
|
||||
description: "Gain 1 Point every second.",
|
||||
cost: new Decimal(1),
|
||||
unlocked() {
|
||||
return player.layers[this.layer].unlocked;
|
||||
} // The upgrade is only visible when this is true
|
||||
},
|
||||
12: {
|
||||
description:
|
||||
"Point generation is faster based on your unspent Lollipops.",
|
||||
cost: new Decimal(1),
|
||||
unlocked() {
|
||||
return hasUpgrade(this.layer, 11);
|
||||
},
|
||||
effect() {
|
||||
// Calculate bonuses from the upgrade. Can return a single value or an object with multiple values
|
||||
let ret = player.layers[this.layer].points
|
||||
.add(1)
|
||||
.pow(
|
||||
player.layers[this.layer].upgrades!.includes(24)
|
||||
? 1.1
|
||||
: player.layers[this.layer].upgrades!.includes(14)
|
||||
? 0.75
|
||||
: 0.5
|
||||
);
|
||||
if (ret.gte("1e20000000")) ret = ret.sqrt().times("1e10000000");
|
||||
return ret;
|
||||
},
|
||||
effectDisplay() {
|
||||
return format(this.effect as Decimal) + "x";
|
||||
} // Add formatting to the effect
|
||||
},
|
||||
13: {
|
||||
unlocked() {
|
||||
return hasUpgrade(this.layer, 12);
|
||||
},
|
||||
onPurchase() {
|
||||
// This function triggers when the upgrade is purchased
|
||||
player.layers[this.layer].unlockOrder = 0;
|
||||
},
|
||||
style() {
|
||||
if (hasUpgrade(this.layer, this.id))
|
||||
return {
|
||||
"background-color": "#1111dd"
|
||||
};
|
||||
else if (!this.canAfford) {
|
||||
return {
|
||||
"background-color": "#dd1111"
|
||||
};
|
||||
} // Otherwise use the default
|
||||
},
|
||||
canAfford() {
|
||||
return player.points.lte(7);
|
||||
},
|
||||
pay() {
|
||||
player.points = player.points.add(7);
|
||||
},
|
||||
fullDisplay:
|
||||
"Only buyable with less than 7 points, and gives you 7 more. Unlocks a secret subtab."
|
||||
},
|
||||
22: {
|
||||
title: "This upgrade doesn't exist",
|
||||
description: "Or does it?.",
|
||||
currencyLocation() {
|
||||
return player.layers[this.layer].buyables;
|
||||
}, // The object in player data that the currency is contained in
|
||||
currencyDisplayName: "exhancers", // Use if using a nonstandard currency
|
||||
currencyInternalName: 11, // Use if using a nonstandard currency
|
||||
|
||||
cost: new Decimal(3),
|
||||
unlocked() {
|
||||
return player.layers[this.layer].unlocked;
|
||||
} // The upgrade is only visible when this is true
|
||||
}
|
||||
}
|
||||
},
|
||||
buyables: {
|
||||
showBRespecButton: true,
|
||||
respec() {
|
||||
// Optional, reset things and give back your currency. Having this function makes a respec button appear
|
||||
player.layers[this.layer].points = player.layers[this.layer].points.add(
|
||||
player.layers[this.layer].spentOnBuyables as Decimal
|
||||
); // A built-in thing to keep track of this but only keeps a single value
|
||||
this.reset();
|
||||
resetLayer(this.layer, true); // Force a reset
|
||||
},
|
||||
respecButtonDisplay: "Respec Thingies", // Text on Respec button, optional
|
||||
respecWarningDisplay:
|
||||
"Are you sure? Respeccing these doesn't accomplish much.",
|
||||
data: {
|
||||
11: {
|
||||
title: "Exhancers", // Optional, displayed at the top in a larger font
|
||||
cost() {
|
||||
// cost for buying xth buyable, can be an object if there are multiple currencies
|
||||
let x = this.amount;
|
||||
if (x.gte(25)) x = x.pow(2).div(25);
|
||||
const cost = Decimal.pow(2, x.pow(1.5));
|
||||
return cost.floor();
|
||||
},
|
||||
effect() {
|
||||
// Effects of owning x of the items, x is a decimal
|
||||
const x = this.amount;
|
||||
const eff = {} as { first?: Decimal; second?: Decimal };
|
||||
if (x.gte(0)) eff.first = Decimal.pow(25, x.pow(1.1));
|
||||
else eff.first = Decimal.pow(1 / 25, x.times(-1).pow(1.1));
|
||||
|
||||
if (x.gte(0)) eff.second = x.pow(0.8);
|
||||
else
|
||||
eff.second = x
|
||||
.times(-1)
|
||||
.pow(0.8)
|
||||
.times(-1);
|
||||
return eff;
|
||||
},
|
||||
display() {
|
||||
// Everything else displayed in the buyable button after the title
|
||||
return (
|
||||
"Cost: " +
|
||||
format(this.cost!) +
|
||||
" lollipops\n\
|
||||
Amount: " +
|
||||
player.layers[this.layer].buyables![this.id] +
|
||||
"/4\n\
|
||||
Adds + " +
|
||||
format((this.effect as { first: Decimal; second: Decimal }).first) +
|
||||
" things and multiplies stuff by " +
|
||||
format((this.effect as { first: Decimal; second: Decimal }).second)
|
||||
);
|
||||
},
|
||||
unlocked() {
|
||||
return player.layers[this.layer].unlocked;
|
||||
},
|
||||
canAfford() {
|
||||
return player.layers[this.layer].points.gte(this.cost!);
|
||||
},
|
||||
buy() {
|
||||
const cost = this.cost!;
|
||||
player.layers[this.layer].points = player.layers[
|
||||
this.layer
|
||||
].points.sub(cost);
|
||||
player.layers[this.layer].buyables![this.id] = player.layers[
|
||||
this.layer
|
||||
].buyables![this.id].add(1);
|
||||
player.layers[this.layer].spentOnBuyables = (player.layers[
|
||||
this.layer
|
||||
].spentOnBuyables as Decimal).add(cost); // This is a built-in system that you can use for respeccing but it only works with a single Decimal value
|
||||
},
|
||||
buyMax() {}, // You'll have to handle this yourself if you want
|
||||
style: { height: "222px" },
|
||||
purchaseLimit: new Decimal(4),
|
||||
sellOne() {
|
||||
const amount = getBuyableAmount(this.layer, this.id)!;
|
||||
if (amount.lte(0)) return; // Only sell one if there is at least one
|
||||
setBuyableAmount(this.layer, this.id, amount.sub(1));
|
||||
player.layers[this.layer].points = player.layers[
|
||||
this.layer
|
||||
].points.add(this.cost!);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onReset(resettingLayer: string) {
|
||||
// Triggers when this layer is being reset, along with the layer doing the resetting. Not triggered by lower layers resetting, but is by layers on the same row.
|
||||
if (
|
||||
layers[resettingLayer].row != undefined &&
|
||||
this.row != undefined &&
|
||||
layers[resettingLayer].row! > this.row!
|
||||
)
|
||||
resetLayerData(this.layer, ["points"]); // This is actually the default behavior
|
||||
},
|
||||
automate() {}, // Do any automation inherent to this layer if appropriate
|
||||
resetsNothing() {
|
||||
return false;
|
||||
},
|
||||
onPrestige() {
|
||||
return;
|
||||
}, // Useful for if you gain secondary resources or have other interesting things happen to this layer when you reset it. You gain the currency after this function ends.
|
||||
|
||||
hotkeys: [
|
||||
{
|
||||
key: "c",
|
||||
description: "reset for lollipops or whatever",
|
||||
press() {
|
||||
if (layers[this.layer].canReset) resetLayer(this.layer);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "ctrl+c",
|
||||
description: "respec things",
|
||||
press() {
|
||||
layers[this.layer].buyables!.respec!();
|
||||
},
|
||||
unlocked() {
|
||||
return hasUpgrade("c", "22");
|
||||
}
|
||||
}
|
||||
],
|
||||
increaseUnlockOrder: [], // Array of layer names to have their order increased when this one is first unlocked
|
||||
|
||||
microtabs: {
|
||||
stuff: {
|
||||
data: {
|
||||
first: {
|
||||
display: `
|
||||
<upgrades />
|
||||
<div>confirmed</div>`
|
||||
},
|
||||
second: {
|
||||
embedLayer: "f"
|
||||
}
|
||||
}
|
||||
},
|
||||
otherStuff: {
|
||||
// There could be another set of microtabs here
|
||||
data: {}
|
||||
}
|
||||
},
|
||||
|
||||
bars: {
|
||||
data: {
|
||||
longBoi: {
|
||||
fillStyle: { "background-color": "#FFFFFF" },
|
||||
baseStyle: { "background-color": "#696969" },
|
||||
textStyle: { color: "#04e050" },
|
||||
|
||||
borderStyle() {
|
||||
return {};
|
||||
},
|
||||
direction: Direction.Right,
|
||||
width: 300,
|
||||
height: 30,
|
||||
progress() {
|
||||
return player.points
|
||||
.add(1)
|
||||
.log(10)
|
||||
.div(10)
|
||||
.toNumber();
|
||||
},
|
||||
display() {
|
||||
return format(player.points) + " / 1e10 points";
|
||||
},
|
||||
unlocked: true
|
||||
},
|
||||
tallBoi: {
|
||||
fillStyle: { "background-color": "#4BEC13" },
|
||||
baseStyle: { "background-color": "#000000" },
|
||||
textStyle: { "text-shadow": "0px 0px 2px #000000" },
|
||||
|
||||
borderStyle() {
|
||||
return { "border-width": "7px" };
|
||||
},
|
||||
direction: Direction.Up,
|
||||
width: 50,
|
||||
height: 200,
|
||||
progress() {
|
||||
return player.points.div(100);
|
||||
},
|
||||
display() {
|
||||
return formatWhole(player.points.div(1).min(100)) + "%";
|
||||
},
|
||||
unlocked: true
|
||||
},
|
||||
flatBoi: {
|
||||
fillStyle: { "background-color": "#FE0102" },
|
||||
baseStyle: { "background-color": "#222222" },
|
||||
textStyle: { "text-shadow": "0px 0px 2px #000000" },
|
||||
|
||||
borderStyle() {
|
||||
return {};
|
||||
},
|
||||
direction: Direction.Up,
|
||||
width: 100,
|
||||
height: 30,
|
||||
progress() {
|
||||
return player.layers.c.points.div(50);
|
||||
},
|
||||
unlocked: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Optional, lets you format the tab yourself by listing components. You can create your own components in v.js.
|
||||
subtabs: {
|
||||
"main tab": {
|
||||
buttonStyle() {
|
||||
return { color: "orange" };
|
||||
},
|
||||
notify: true,
|
||||
display: `
|
||||
<main-display />
|
||||
<sticky><prestige-button /></sticky>
|
||||
<resource-display />
|
||||
<spacer height="5px" />
|
||||
<button onclick='console.log("yeet")'>'HI'</button>
|
||||
<div>Name your points!</div>
|
||||
<TextField :value="player.layers.c.thingy" @input="value => player.layers.c.thingy = value" :field="false" />
|
||||
<sticky style="color: red; font-size: 32px; font-family: Comic Sans MS;">I have {{ format(player.points) }} {{ player.layers.c.thingy }} points!</sticky>
|
||||
<hr />
|
||||
<milestones />
|
||||
<spacer />
|
||||
<upgrades />
|
||||
<challenges />`,
|
||||
glowColor: "blue"
|
||||
},
|
||||
thingies: {
|
||||
resetNotify: true,
|
||||
style() {
|
||||
return { "background-color": "#222222", "--background": "#222222" };
|
||||
},
|
||||
buttonStyle() {
|
||||
return { "border-color": "orange" };
|
||||
},
|
||||
display: `
|
||||
<buyables />
|
||||
<spacer />
|
||||
<row style="width: 600px; height: 350px; background-color: green; border-style: solid;">
|
||||
<Toggle :value="player.layers.c.beep" @change="value => player.layers.c.beep = value" />
|
||||
<spacer width="30px" height="10px" />
|
||||
<div>Beep</div>
|
||||
<spacer />
|
||||
<vr height="200px"/>
|
||||
<column>
|
||||
<prestige-button style="width: 150px; height: 80px" />
|
||||
<prestige-button style="width: 100px; height: 150px" />
|
||||
</column>
|
||||
</row>
|
||||
<spacer />
|
||||
<img src="https://unsoftcapped2.github.io/The-Modding-Tree-2/discord.png" />`
|
||||
},
|
||||
jail: {
|
||||
display: `
|
||||
<infobox id="coolInfo" />
|
||||
<bar id="longBoi" />
|
||||
<spacer />
|
||||
<row>
|
||||
<column style="background-color: #555555; padding: 15px">
|
||||
<div style="color: teal">Sugar level:</div><spacer /><bar id="tallBoi" />
|
||||
</column>
|
||||
<spacer />
|
||||
<column>
|
||||
<div>idk</div>
|
||||
<spacer width="0" height="50px" />
|
||||
<bar id="flatBoi" />
|
||||
</column>
|
||||
</row>
|
||||
<spacer />
|
||||
<div>It's jail because "bars"! So funny! Ha ha!</div>
|
||||
<tree :nodes="[['f', 'c'], ['g', 'spook', 'h']]" />`
|
||||
},
|
||||
illuminati: {
|
||||
unlocked() {
|
||||
return hasUpgrade("c", 13);
|
||||
},
|
||||
display: `
|
||||
<h1> C O N F I R M E D </h1>
|
||||
<spacer />
|
||||
<microtab family="stuff" style="width: 660px; height: 370px; background-color: brown; --background: brown; border: solid white; margin: auto" />
|
||||
<div>Adjust how many points H gives you!</div>
|
||||
<Slider :value="player.layers.c.otherThingy" @change="value => player.c.otherThingy = value" :min="1" :max="30" />`
|
||||
}
|
||||
},
|
||||
style() {
|
||||
return {
|
||||
//'background-color': '#3325CC'
|
||||
};
|
||||
},
|
||||
nodeStyle() {
|
||||
return {
|
||||
// Style on the layer node
|
||||
color: "#3325CC",
|
||||
"text-decoration": "underline"
|
||||
};
|
||||
},
|
||||
glowColor: "orange", // If the node is highlighted, it will be this color (default is red)
|
||||
componentStyles: {
|
||||
challenge() {
|
||||
return { height: "200px" };
|
||||
},
|
||||
"prestige-button"() {
|
||||
return { color: "#AA66AA" };
|
||||
}
|
||||
},
|
||||
tooltip() {
|
||||
// Optional, tooltip displays when the layer is unlocked
|
||||
let tooltip = "{{ formatWhole(player.layers.c.points) }} {{ layers.c.resource }}";
|
||||
if (player.layers[this.layer].buyables![11].gt(0))
|
||||
tooltip +=
|
||||
"<br><i><br><br><br>{{ formatWhole(player.layers.c.buyables![11]) }} Exhancers</i>";
|
||||
return tooltip;
|
||||
},
|
||||
shouldNotify() {
|
||||
// Optional, layer will be highlighted on the tree if true.
|
||||
// Layer will automatically highlight if an upgrade is purchasable.
|
||||
return player.layers.c.buyables![11] == new Decimal(1);
|
||||
},
|
||||
mark: "https://unsoftcapped2.github.io/The-Modding-Tree-2/discord.png",
|
||||
resetDescription: "Melt your points into "
|
||||
} as RawLayer;
|
|
@ -1,107 +0,0 @@
|
|||
import Decimal, { formatWhole } from '../../../util/bignum';
|
||||
import player from '../../../game/player';
|
||||
import { layers as tmp } from '../../../game/layers';
|
||||
import { getClickableState } from '../../../util/features';
|
||||
|
||||
export default {
|
||||
id: "f",
|
||||
infoboxes:{
|
||||
coolInfo: {
|
||||
title: "Lore",
|
||||
titleStyle: {'color': '#FE0000'},
|
||||
body: "DEEP LORE!",
|
||||
bodyStyle: {'background-color': "#0000EE"}
|
||||
}
|
||||
},
|
||||
|
||||
startData() { return {
|
||||
unlocked: false,
|
||||
points: new Decimal(0),
|
||||
boop: false,
|
||||
clickables: {[11]: "Start"}, // Optional default Clickable state
|
||||
}},
|
||||
color: "#FE0102",
|
||||
requires() {return new Decimal(10)},
|
||||
resource: "farm points",
|
||||
baseResource: "points",
|
||||
baseAmount() {return player.points},
|
||||
type: "static",
|
||||
exponent: 0.5,
|
||||
base: 3,
|
||||
roundUpCost: true,
|
||||
canBuyMax() {return false},
|
||||
name: "Farms",
|
||||
//directMult() {return new Decimal(player.c.otherThingy)},
|
||||
|
||||
row: 1,
|
||||
branches: [{ target: "c", 'stroke-width': '25px', 'stroke': 'blue', style: 'filter: blur(5px)' }], // When this layer appears, a branch will appear from this layer to any layers here. Each entry can be a pair consisting of a layer id and a color.
|
||||
|
||||
tooltipLocked() { // Optional, tooltip displays when the layer is locked
|
||||
return ("This weird farmer dinosaur will only see you if you have at least {{layers.f.requires}} points. You only have {{ formatWhole(player.points) }}")
|
||||
},
|
||||
midsection: '<div><br/><img src="https://images.beano.com/store/24ab3094eb95e5373bca1ccd6f330d4406db8d1f517fc4170b32e146f80d?auto=compress%2Cformat&dpr=1&w=390" /><div>Bork Bork!</div></div>',
|
||||
// The following are only currently used for "custom" Prestige type:
|
||||
prestigeButtonDisplay() { //Is secretly HTML
|
||||
if (!this.canBuyMax) return "Hi! I'm a <u>weird dinosaur</u> and I'll give you a Farm Point in exchange for all of your points and lollipops! (At least " + formatWhole(tmp[this.layer].nextAt) + " points)"
|
||||
if (this.canBuyMax) return "Hi! I'm a <u>weird dinosaur</u> and I'll give you <b>" + formatWhole(tmp[this.layer].resetGain) + "</b> Farm Points in exchange for all of your points and lollipops! (You'll get another one at " + formatWhole(tmp[this.layer].nextAtDisp) + " points)"
|
||||
},
|
||||
canReset() {
|
||||
return tmp[this.layer].baseAmount.gte(tmp[this.layer].nextAt)
|
||||
},
|
||||
// This is also non minimal, a Clickable!
|
||||
clickables: {
|
||||
|
||||
masterButtonClick() {
|
||||
if (getClickableState(this.layer, 11) == "Borkened...")
|
||||
player[this.layer].clickables[11] = "Start"
|
||||
},
|
||||
masterButtonDisplay() {return (getClickableState(this.layer, 11) == "Borkened...") ? "Fix the clickable!" : "Does nothing"}, // Text on Respec button, optional
|
||||
11: {
|
||||
title: "Clicky clicky!", // Optional, displayed at the top in a larger font
|
||||
display() { // Everything else displayed in the buyable button after the title
|
||||
let data = getClickableState(this.layer, this.id)
|
||||
return "Current state:<br>" + data
|
||||
},
|
||||
unlocked() { return player[this.layer].unlocked },
|
||||
canClick() {
|
||||
return getClickableState(this.layer, this.id) !== "Borkened..."},
|
||||
click() {
|
||||
switch(getClickableState(this.layer, this.id)){
|
||||
case "Start":
|
||||
player[this.layer].clickables[this.id] = "A new state!"
|
||||
break;
|
||||
case "A new state!":
|
||||
player[this.layer].clickables[this.id] = "Keep going!"
|
||||
break;
|
||||
case "Keep going!":
|
||||
player[this.layer].clickables[this.id] = "Maybe that's a bit too far..."
|
||||
break;
|
||||
case "Maybe that's a bit too far...":
|
||||
//makeParticles(coolParticle, 4)
|
||||
player[this.layer].clickables[this.id] = "Borkened..."
|
||||
break;
|
||||
default:
|
||||
player[this.layer].clickables[this.id] = "Start"
|
||||
break;
|
||||
}
|
||||
},
|
||||
hold(){
|
||||
console.log("Clickkkkk...")
|
||||
},
|
||||
style() {
|
||||
switch(getClickableState(this.layer, this.id)){
|
||||
case "Start":
|
||||
return {'background-color': 'green'}
|
||||
case "A new state!":
|
||||
return {'background-color': 'yellow'}
|
||||
case "Keep going!":
|
||||
return {'background-color': 'orange'}
|
||||
case "Maybe that's a bit too far...":
|
||||
return {'background-color': 'red'}
|
||||
default:
|
||||
return {}
|
||||
}},
|
||||
},
|
||||
},
|
||||
|
||||
}
|
151
src/data/layers/aca/f.ts
Normal file
151
src/data/layers/aca/f.ts
Normal file
|
@ -0,0 +1,151 @@
|
|||
/* eslint-disable */
|
||||
import { layers as tmp } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { RawLayer } from "@/typings/layer";
|
||||
import Decimal, { formatWhole } from "@/util/bignum";
|
||||
import { getClickableState } from "@/util/features";
|
||||
|
||||
export default {
|
||||
id: "f",
|
||||
infoboxes: {
|
||||
data: {
|
||||
coolInfo: {
|
||||
title: "Lore",
|
||||
titleStyle: { color: "#FE0000" },
|
||||
body: "DEEP LORE!",
|
||||
bodyStyle: { "background-color": "#0000EE" }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
startData() {
|
||||
return {
|
||||
unlocked: false,
|
||||
points: new Decimal(0),
|
||||
boop: false,
|
||||
clickables: { [11]: "Start" } // Optional default Clickable state
|
||||
};
|
||||
},
|
||||
color: "#FE0102",
|
||||
requires() {
|
||||
return new Decimal(10);
|
||||
},
|
||||
resource: "farm points",
|
||||
baseResource: "points",
|
||||
baseAmount() {
|
||||
return player.points;
|
||||
},
|
||||
type: "static",
|
||||
exponent: 0.5,
|
||||
base: 3,
|
||||
roundUpCost: true,
|
||||
canBuyMax() {
|
||||
return false;
|
||||
},
|
||||
name: "Farms",
|
||||
//directMult() {return new Decimal(player.c.otherThingy)},
|
||||
|
||||
row: 1,
|
||||
branches: [
|
||||
{
|
||||
target: "c",
|
||||
"stroke-width": "25px",
|
||||
stroke: "blue",
|
||||
style: "filter: blur(5px)"
|
||||
}
|
||||
], // When this layer appears, a branch will appear from this layer to any layers here. Each entry can be a pair consisting of a layer id and a color.
|
||||
|
||||
tooltipLocked() {
|
||||
// Optional, tooltip displays when the layer is locked
|
||||
return "This weird farmer dinosaur will only see you if you have at least {{layers.f.requires}} points. You only have {{ formatWhole(player.points) }}";
|
||||
},
|
||||
midsection:
|
||||
'<div><br/><img src="https://images.beano.com/store/24ab3094eb95e5373bca1ccd6f330d4406db8d1f517fc4170b32e146f80d?auto=compress%2Cformat&dpr=1&w=390" /><div>Bork Bork!</div></div>',
|
||||
// The following are only currently used for "custom" Prestige type:
|
||||
prestigeButtonDisplay() {
|
||||
//Is secretly HTML
|
||||
if (!this.canBuyMax)
|
||||
return (
|
||||
"Hi! I'm a <u>weird dinosaur</u> and I'll give you a Farm Point in exchange for all of your points and lollipops! (At least " +
|
||||
formatWhole(tmp[this.layer].nextAt) +
|
||||
" points)"
|
||||
);
|
||||
if (this.canBuyMax)
|
||||
return (
|
||||
"Hi! I'm a <u>weird dinosaur</u> and I'll give you <b>" +
|
||||
formatWhole(tmp[this.layer].resetGain) +
|
||||
"</b> Farm Points in exchange for all of your points and lollipops! (You'll get another one at " +
|
||||
formatWhole(tmp[this.layer].nextAt) +
|
||||
" points)"
|
||||
);
|
||||
},
|
||||
canReset() {
|
||||
return Decimal.gte(tmp[this.layer].baseAmount!, tmp[this.layer].nextAt);
|
||||
},
|
||||
// This is also non minimal, a Clickable!
|
||||
clickables: {
|
||||
masterButtonClick() {
|
||||
if (getClickableState(this.layer, 11) == "Borkened...")
|
||||
player.layers[this.layer].clickables![11] = "Start";
|
||||
},
|
||||
masterButtonDisplay() {
|
||||
return getClickableState(this.layer, 11) == "Borkened..."
|
||||
? "Fix the clickable!"
|
||||
: "Does nothing";
|
||||
}, // Text on Respec button, optional
|
||||
data: {
|
||||
11: {
|
||||
title: "Clicky clicky!", // Optional, displayed at the top in a larger font
|
||||
display() {
|
||||
// Everything else displayed in the buyable button after the title
|
||||
const data = getClickableState(this.layer, this.id);
|
||||
return "Current state:<br>" + data;
|
||||
},
|
||||
unlocked() {
|
||||
return player.layers[this.layer].unlocked;
|
||||
},
|
||||
canClick() {
|
||||
return getClickableState(this.layer, this.id) !== "Borkened...";
|
||||
},
|
||||
click() {
|
||||
switch (getClickableState(this.layer, this.id)) {
|
||||
case "Start":
|
||||
player.layers[this.layer].clickables![this.id] = "A new state!";
|
||||
break;
|
||||
case "A new state!":
|
||||
player.layers[this.layer].clickables![this.id] = "Keep going!";
|
||||
break;
|
||||
case "Keep going!":
|
||||
player.layers[this.layer].clickables![this.id] =
|
||||
"Maybe that's a bit too far...";
|
||||
break;
|
||||
case "Maybe that's a bit too far...":
|
||||
//makeParticles(coolParticle, 4)
|
||||
player.layers[this.layer].clickables![this.id] = "Borkened...";
|
||||
break;
|
||||
default:
|
||||
player.layers[this.layer].clickables![this.id] = "Start";
|
||||
break;
|
||||
}
|
||||
},
|
||||
hold() {
|
||||
console.log("Clickkkkk...");
|
||||
},
|
||||
style() {
|
||||
switch (getClickableState(this.layer, this.id)) {
|
||||
case "Start":
|
||||
return { "background-color": "green" };
|
||||
case "A new state!":
|
||||
return { "background-color": "yellow" };
|
||||
case "Keep going!":
|
||||
return { "background-color": "orange" };
|
||||
case "Maybe that's a bit too far...":
|
||||
return { "background-color": "red" };
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} as RawLayer;
|
|
@ -1,153 +0,0 @@
|
|||
import Decimal, { format } from '../../util/bignum';
|
||||
import player from '../../game/player';
|
||||
import { layers } from '../../game/layers';
|
||||
import { hasUpgrade, hasMilestone, getBuyableAmount, setBuyableAmount, hasChallenge } from '../../util/features';
|
||||
import { resetLayer } from '../../util/layers';
|
||||
|
||||
export default {
|
||||
id: "i",
|
||||
position: 2, // Horizontal position within a row. By default it uses the layer id and sorts in alphabetical order
|
||||
startData() { return {
|
||||
unlocked: false,
|
||||
points: new Decimal(0),
|
||||
|
||||
}},
|
||||
branches: ["p"],
|
||||
color: "#964B00",
|
||||
requires(){
|
||||
let require = new Decimal(8).plus(player.i.points.div(10).floor().times(2))
|
||||
return require
|
||||
} , // Can be a function that takes requirement increases into account
|
||||
effectDisplay(){return "Multiplying points and prestige points by "+format(player[this.layer].points.plus(1).pow(hasUpgrade("p",235)?6.9420:1))},
|
||||
resource: "Infinity", // Name of prestige currency
|
||||
baseResource: "pointy points", // Name of resource prestige is based on
|
||||
baseAmount() {return player.p.buyables[21]}, // Get the current amount of baseResource
|
||||
type: "custom", // normal: cost to gain currency depends on amount gained. static: cost depends on how much you already have
|
||||
resetGain(){
|
||||
|
||||
if (hasMilestone("p",12)){return getBuyableAmount("p",21).div(2).floor().times(2).times(5).sub(30).sub(player.i.points)}
|
||||
return (player.p.buyables[21].gte(layers.i.requires)?1:0)}, // Prestige currency exponent
|
||||
getNextAt(){return new Decimal(100)},
|
||||
canReset(){return player.p.buyables[21].gte(layers.i.requires)},
|
||||
prestigeButtonDisplay(){return "Reset everything for +"+format(layers.i.resetGain)+" Infinity.<br>You need "+format(layers.i.requires)+" pointy points to reset."},
|
||||
row: 1, // Row the layer is in on the tree (0 is the first row)
|
||||
hotkeys: [
|
||||
{key: "i", description: "I: Infinity", press(){if (layers.i.canReset) resetLayer(this.layer)}},
|
||||
],
|
||||
layerShown(){return player[this.layer].unlocked||new Decimal(player.p.buyables[21]).gte(8)},
|
||||
milestones: {
|
||||
0: {
|
||||
requirementDisplay: "2 Infinity points",
|
||||
effectDisplay: "Keep ALL milestones on reset",
|
||||
done() { return player[this.layer].points.gte(2) },
|
||||
|
||||
},
|
||||
1: {
|
||||
requirementDisplay: "3 Infinity points",
|
||||
effectDisplay: "Pointy points don't reset generators",
|
||||
done() { return player[this.layer].points.gte(3) },
|
||||
unlocked(){return hasMilestone(this.layer,this.id-1)}
|
||||
},
|
||||
2: {
|
||||
requirementDisplay: "4 Infinity points",
|
||||
effectDisplay: "Start with 6 <b>Time Dilation</b>, 3 <b>Point</b>, and 1 of the other 2 challenges",
|
||||
done() { return player[this.layer].points.gte(4) },
|
||||
unlocked(){return hasMilestone(this.layer,this.id-1)}
|
||||
},
|
||||
3: {
|
||||
requirementDisplay: "5 Infinity points",
|
||||
effectDisplay: "Start with 40 upgrades and 6 boosts",
|
||||
done() { return player[this.layer].points.gte(5) },
|
||||
unlocked(){return hasMilestone(this.layer,this.id-1)}
|
||||
},
|
||||
4: {
|
||||
requirementDisplay: "6 Infinity points",
|
||||
effectDisplay: "You can choose all of the 14th row upgrades, and remove the respec button",
|
||||
done() { return player[this.layer].points.gte(6) },
|
||||
unlocked(){return hasMilestone(this.layer,this.id-1)}
|
||||
},
|
||||
5: {
|
||||
requirementDisplay: "8 Infinity points",
|
||||
effectDisplay: "Keep all upgrades and 7 Time dilation",
|
||||
done() { return player[this.layer].points.gte(8) },
|
||||
unlocked(){return hasMilestone(this.layer,this.id-1)}
|
||||
},
|
||||
6: {
|
||||
requirementDisplay: "10 Infinity points",
|
||||
effectDisplay: "Infinity reset nothing and auto prestige",
|
||||
done() { return player[this.layer].points.gte(10) },
|
||||
unlocked(){return hasMilestone(this.layer,this.id-1)}
|
||||
},
|
||||
},
|
||||
resetsNothing(){return hasMilestone(this.layer,6)},
|
||||
update(){
|
||||
if (hasMilestone(this.layer,0)){
|
||||
if (!hasMilestone("p",0)){
|
||||
player.p.milestones.push(0)
|
||||
player.p.milestones.push(1)
|
||||
player.p.milestones.push(2)
|
||||
player.p.milestones.push(3)
|
||||
player.p.milestones.push(4)
|
||||
player.p.milestones.push(5)
|
||||
player.p.milestones.push(6)
|
||||
player.p.milestones.push(7)
|
||||
player.p.milestones.push(8)
|
||||
}
|
||||
}
|
||||
if (hasMilestone(this.layer,2)){
|
||||
if (!hasChallenge("p",11)){
|
||||
player.p.challenges[11]=new Decimal(hasMilestone(this.layer,5)?7:6)
|
||||
player.p.challenges[12]=new Decimal(3)
|
||||
player.p.challenges[21]=new Decimal(1)
|
||||
player.p.challenges[22]=new Decimal(1)
|
||||
}
|
||||
}
|
||||
if (hasMilestone(this.layer,3)){
|
||||
if (!hasUpgrade("p",71)){
|
||||
player.p.upgrades=[11,12,13,14,21,22,23,24,31,32,33,34,41,42,43,44,51,52,53,54,61,62,63,64,71,72,73,74,81,82,83,84,91,92,93,94,101,102,103,104]
|
||||
}
|
||||
if (getBuyableAmount("p",11).lt(6)){setBuyableAmount("p",11,new Decimal(6))}
|
||||
}
|
||||
if (hasUpgrade(this.layer,13)){
|
||||
|
||||
for (let i=0;i<(hasUpgrade("p",222)?100:hasUpgrade("p",215)?10:1);i++){
|
||||
if (layers.p.buyables[12].canAfford)layers.p.buyables[12].buy()
|
||||
if (layers.p.buyables[13].canAfford)layers.p.buyables[13].buy()
|
||||
if (layers.p.buyables[14].canAfford&&layers.p.buyables[14].unlocked())layers.p.buyables[14].buy()
|
||||
if (layers.p.buyables[21].canAfford)layers.p.buyables[21].buy()}
|
||||
}
|
||||
if (hasUpgrade("p",223)){
|
||||
if (hasMilestone("p",14))player.p.buyables[22]=player.p.buyables[22].max(player.p.buyables[21].sub(7))
|
||||
else if (layers.p.buyables[22].canAfford)layers.p.buyables[22].buy()
|
||||
}
|
||||
if (hasMilestone(this.layer,5)&&!hasUpgrade("p",111)){player.p.upgrades=[11,12,13,14,21,22,23,24,31,32,33,34,41,42,43,44,51,52,53,54,61,62,63,64,71,72,73,74,81,82,83,84,91,92,93,94,101,102,103,104,111,121,122,131,132,141,142,143]}
|
||||
if (hasMilestone(this.layer,6)) {
|
||||
this.reset();
|
||||
}
|
||||
},
|
||||
upgrades:{
|
||||
rows: 999,
|
||||
cols: 5,
|
||||
11:{
|
||||
title: "Prestige",
|
||||
description: "Gain 100% of prestige points per second",
|
||||
cost(){
|
||||
return new Decimal(1)},
|
||||
unlocked(){return hasMilestone(this.layer,4)}
|
||||
},
|
||||
12:{
|
||||
title: "Automation",
|
||||
description: "Remove the nerf of upgrade <b>Active</b>",
|
||||
cost(){
|
||||
return new Decimal(2)},
|
||||
unlocked(){return hasUpgrade(this.layer,11)}
|
||||
},
|
||||
13:{
|
||||
title: "Pointy",
|
||||
description: "Automatically buy generators and pointy points",
|
||||
cost(){
|
||||
return new Decimal(5)},
|
||||
unlocked(){return hasUpgrade(this.layer,11)}
|
||||
},
|
||||
}
|
||||
}
|
354
src/data/layers/demo-infinity.ts
Normal file
354
src/data/layers/demo-infinity.ts
Normal file
|
@ -0,0 +1,354 @@
|
|||
/* eslint-disable */
|
||||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { Layer, RawLayer } from "@/typings/layer";
|
||||
import Decimal, { format } from "@/util/bignum";
|
||||
import {
|
||||
getBuyableAmount, hasChallenge, hasMilestone, hasUpgrade, setBuyableAmount
|
||||
} from "@/util/features";
|
||||
import { resetLayer } from "@/util/layers";
|
||||
|
||||
export default {
|
||||
id: "i",
|
||||
position: 2, // Horizontal position within a row. By default it uses the layer id and sorts in alphabetical order
|
||||
startData() {
|
||||
return {
|
||||
unlocked: false,
|
||||
points: new Decimal(0)
|
||||
};
|
||||
},
|
||||
branches: ["p"],
|
||||
color: "#964B00",
|
||||
requires() {
|
||||
const require = new Decimal(8).plus(
|
||||
player.layers.i.points
|
||||
.div(10)
|
||||
.floor()
|
||||
.times(2)
|
||||
);
|
||||
return require;
|
||||
}, // Can be a function that takes requirement increases into account
|
||||
effectDisplay() {
|
||||
return (
|
||||
"Multiplying points and prestige points by " +
|
||||
format(
|
||||
player.layers[this.layer].points
|
||||
.plus(1)
|
||||
.pow(hasUpgrade("p", 235) ? 6.942 : 1)
|
||||
)
|
||||
);
|
||||
},
|
||||
resource: "Infinity", // Name of prestige currency
|
||||
baseResource: "pointy points", // Name of resource prestige is based on
|
||||
baseAmount() {
|
||||
return player.layers.p.buyables![21];
|
||||
}, // Get the current amount of baseResource
|
||||
type: "custom", // normal: cost to gain currency depends on amount gained. static: cost depends on how much you already have
|
||||
resetGain() {
|
||||
if (hasMilestone("p", 12)) {
|
||||
return getBuyableAmount("p", 21)!
|
||||
.div(2)
|
||||
.floor()
|
||||
.times(2)
|
||||
.times(5)
|
||||
.sub(30)
|
||||
.sub(player.layers.i.points);
|
||||
}
|
||||
return player.layers.p.buyables![21].gte(layers.i.requires!) ? 1 : 0;
|
||||
}, // Prestige currency exponent
|
||||
getNextAt() {
|
||||
return new Decimal(100);
|
||||
},
|
||||
canReset() {
|
||||
return player.layers.p.buyables![21].gte(layers.i.requires!);
|
||||
},
|
||||
prestigeButtonDisplay() {
|
||||
return (
|
||||
"Reset everything for +" +
|
||||
format(layers.i.resetGain) +
|
||||
" Infinity.<br>You need " +
|
||||
format(layers.i.requires!) +
|
||||
" pointy points to reset."
|
||||
);
|
||||
},
|
||||
row: 1, // Row the layer is in on the tree (0 is the first row)
|
||||
hotkeys: [
|
||||
{
|
||||
key: "i",
|
||||
description: "I: Infinity",
|
||||
press() {
|
||||
if (layers.i.canReset) resetLayer(this.layer);
|
||||
}
|
||||
}
|
||||
],
|
||||
layerShown() {
|
||||
return (
|
||||
player.layers[this.layer].unlocked ||
|
||||
new Decimal(player.layers.p.buyables[21]).gte(8)
|
||||
);
|
||||
},
|
||||
milestones: {
|
||||
data: {
|
||||
0: {
|
||||
requirementDisplay: "2 Infinity points",
|
||||
effectDisplay: "Keep ALL milestones on reset",
|
||||
done() {
|
||||
return player.layers[this.layer].points.gte(2);
|
||||
}
|
||||
},
|
||||
1: {
|
||||
requirementDisplay: "3 Infinity points",
|
||||
effectDisplay: "Pointy points don't reset generators",
|
||||
done() {
|
||||
return player.layers[this.layer].points.gte(3);
|
||||
},
|
||||
unlocked() {
|
||||
return hasMilestone(this.layer, Number(this.id) - 1);
|
||||
}
|
||||
},
|
||||
2: {
|
||||
requirementDisplay: "4 Infinity points",
|
||||
effectDisplay:
|
||||
"Start with 6 <b>Time Dilation</b>, 3 <b>Point</b>, and 1 of the other 2 challenges",
|
||||
done() {
|
||||
return player.layers[this.layer].points.gte(4);
|
||||
},
|
||||
unlocked() {
|
||||
return hasMilestone(this.layer, Number(this.id) - 1);
|
||||
}
|
||||
},
|
||||
3: {
|
||||
requirementDisplay: "5 Infinity points",
|
||||
effectDisplay: "Start with 40 upgrades and 6 boosts",
|
||||
done() {
|
||||
return player.layers[this.layer].points.gte(5);
|
||||
},
|
||||
unlocked() {
|
||||
return hasMilestone(this.layer, Number(this.id) - 1);
|
||||
}
|
||||
},
|
||||
4: {
|
||||
requirementDisplay: "6 Infinity points",
|
||||
effectDisplay:
|
||||
"You can choose all of the 14th row upgrades, and remove the respec button",
|
||||
done() {
|
||||
return player.layers[this.layer].points.gte(6);
|
||||
},
|
||||
unlocked() {
|
||||
return hasMilestone(this.layer, Number(this.id) - 1);
|
||||
}
|
||||
},
|
||||
5: {
|
||||
requirementDisplay: "8 Infinity points",
|
||||
effectDisplay: "Keep all upgrades and 7 Time dilation",
|
||||
done() {
|
||||
return player.layers[this.layer].points.gte(8);
|
||||
},
|
||||
unlocked() {
|
||||
return hasMilestone(this.layer, Number(this.id) - 1);
|
||||
}
|
||||
},
|
||||
6: {
|
||||
requirementDisplay: "10 Infinity points",
|
||||
effectDisplay: "Infinity reset nothing and auto prestige",
|
||||
done() {
|
||||
return player.layers[this.layer].points.gte(10);
|
||||
},
|
||||
unlocked() {
|
||||
return hasMilestone(this.layer, Number(this.id) - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
resetsNothing() {
|
||||
return hasMilestone(this.layer, 6);
|
||||
},
|
||||
update(this: Layer) {
|
||||
if (hasMilestone(this.layer, 0)) {
|
||||
if (!hasMilestone("p", 0)) {
|
||||
player.layers.p.milestones!.push(0);
|
||||
player.layers.p.milestones!.push(1);
|
||||
player.layers.p.milestones!.push(2);
|
||||
player.layers.p.milestones!.push(3);
|
||||
player.layers.p.milestones!.push(4);
|
||||
player.layers.p.milestones!.push(5);
|
||||
player.layers.p.milestones!.push(6);
|
||||
player.layers.p.milestones!.push(7);
|
||||
player.layers.p.milestones!.push(8);
|
||||
}
|
||||
}
|
||||
if (hasMilestone(this.layer, 2)) {
|
||||
if (!hasChallenge("p", 11)) {
|
||||
player.layers.p.challenges![11] = new Decimal(
|
||||
hasMilestone(this.layer, 5) ? 7 : 6
|
||||
);
|
||||
player.layers.p.challenges![12] = new Decimal(3);
|
||||
player.layers.p.challenges![21] = new Decimal(1);
|
||||
player.layers.p.challenges![22] = new Decimal(1);
|
||||
}
|
||||
}
|
||||
if (hasMilestone(this.layer, 3)) {
|
||||
if (!hasUpgrade("p", 71)) {
|
||||
player.layers.p.upgrades = [
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24,
|
||||
31,
|
||||
32,
|
||||
33,
|
||||
34,
|
||||
41,
|
||||
42,
|
||||
43,
|
||||
44,
|
||||
51,
|
||||
52,
|
||||
53,
|
||||
54,
|
||||
61,
|
||||
62,
|
||||
63,
|
||||
64,
|
||||
71,
|
||||
72,
|
||||
73,
|
||||
74,
|
||||
81,
|
||||
82,
|
||||
83,
|
||||
84,
|
||||
91,
|
||||
92,
|
||||
93,
|
||||
94,
|
||||
101,
|
||||
102,
|
||||
103,
|
||||
104
|
||||
];
|
||||
}
|
||||
if (getBuyableAmount("p", 11)!.lt(6)) {
|
||||
setBuyableAmount("p", 11, new Decimal(6));
|
||||
}
|
||||
}
|
||||
if (hasUpgrade(this.layer, 13)) {
|
||||
for (
|
||||
let i = 0;
|
||||
i < (hasUpgrade("p", 222) ? 100 : hasUpgrade("p", 215) ? 10 : 1);
|
||||
i++
|
||||
) {
|
||||
if (layers.p.buyables!.data[12].canAfford) layers.p.buyables!.data[12].buy();
|
||||
if (layers.p.buyables!.data[13].canAfford) layers.p.buyables!.data[13].buy();
|
||||
if (
|
||||
layers.p.buyables!.data[14].canAfford &&
|
||||
layers.p.buyables!.data[14].unlocked
|
||||
)
|
||||
layers.p.buyables!.data[14].buy();
|
||||
if (layers.p.buyables!.data[21].canAfford) layers.p.buyables!.data[21].buy();
|
||||
}
|
||||
}
|
||||
if (hasUpgrade("p", 223)) {
|
||||
if (hasMilestone("p", 14))
|
||||
player.layers.p.buyables![22] = player.layers.p.buyables![22].max(
|
||||
player.layers.p.buyables![21].sub(7)
|
||||
);
|
||||
else if (layers.p.buyables!.data[22].canAfford) layers.p.buyables!.data[22].buy();
|
||||
}
|
||||
if (hasMilestone(this.layer, 5) && !hasUpgrade("p", 111)) {
|
||||
player.layers.p.upgrades = [
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24,
|
||||
31,
|
||||
32,
|
||||
33,
|
||||
34,
|
||||
41,
|
||||
42,
|
||||
43,
|
||||
44,
|
||||
51,
|
||||
52,
|
||||
53,
|
||||
54,
|
||||
61,
|
||||
62,
|
||||
63,
|
||||
64,
|
||||
71,
|
||||
72,
|
||||
73,
|
||||
74,
|
||||
81,
|
||||
82,
|
||||
83,
|
||||
84,
|
||||
91,
|
||||
92,
|
||||
93,
|
||||
94,
|
||||
101,
|
||||
102,
|
||||
103,
|
||||
104,
|
||||
111,
|
||||
121,
|
||||
122,
|
||||
131,
|
||||
132,
|
||||
141,
|
||||
142,
|
||||
143
|
||||
];
|
||||
}
|
||||
if (hasMilestone(this.layer, 6)) {
|
||||
this.reset();
|
||||
}
|
||||
},
|
||||
upgrades: {
|
||||
rows: 999,
|
||||
cols: 5,
|
||||
data: {
|
||||
11: {
|
||||
title: "Prestige",
|
||||
description: "Gain 100% of prestige points per second",
|
||||
cost() {
|
||||
return new Decimal(1);
|
||||
},
|
||||
unlocked() {
|
||||
return hasMilestone(this.layer, 4);
|
||||
}
|
||||
},
|
||||
12: {
|
||||
title: "Automation",
|
||||
description: "Remove the nerf of upgrade <b>Active</b>",
|
||||
cost() {
|
||||
return new Decimal(2);
|
||||
},
|
||||
unlocked() {
|
||||
return hasUpgrade(this.layer, 11);
|
||||
}
|
||||
},
|
||||
13: {
|
||||
title: "Pointy",
|
||||
description: "Automatically buy generators and pointy points",
|
||||
cost() {
|
||||
return new Decimal(5);
|
||||
},
|
||||
unlocked() {
|
||||
return hasUpgrade(this.layer, 11);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} as RawLayer;
|
|
@ -1,940 +0,0 @@
|
|||
import Decimal, { format } from '../../util/bignum';
|
||||
import player from '../../game/player';
|
||||
import { layers } from '../../game/layers';
|
||||
import { hasUpgrade, hasMilestone, getBuyableAmount, setBuyableAmount, hasChallenge } from '../../util/features';
|
||||
import { resetLayer } from '../../util/layers';
|
||||
|
||||
export default {
|
||||
id: "p",
|
||||
position: 2,
|
||||
startData() { return {
|
||||
unlocked: true,
|
||||
points: new Decimal(0),
|
||||
gp: new Decimal(0),
|
||||
g: new Decimal(0),
|
||||
geff: new Decimal(1),
|
||||
cmult: new Decimal(1),
|
||||
}},
|
||||
color: "#4BDC13",
|
||||
requires(){
|
||||
let require = new Decimal(68.99)
|
||||
if (hasMilestone(this.layer,0))require=require.plus(0.01)
|
||||
if (hasUpgrade(this.layer,21))require=require.tetrate(hasUpgrade("p",34)?(new Decimal(1).div(new Decimal(1).plus(layers.p.upgrades[34].effect))):1)
|
||||
if (hasUpgrade(this.layer,22))require=require.pow(hasUpgrade("p",34)?(new Decimal(1).div(new Decimal(1).plus(layers.p.upgrades[34].effect))):1)
|
||||
if (hasUpgrade(this.layer,23))require=require.div(hasUpgrade("p",34)?(new Decimal(1).plus(layers.p.upgrades[34].effect)):1)
|
||||
if (hasUpgrade(this.layer,24))require=require.sub(hasUpgrade("p",34)?(new Decimal(1).plus(layers.p.upgrades[34].effect)):1)
|
||||
return require.max(1)
|
||||
},
|
||||
resource: "prestige points",
|
||||
baseResource: "points",
|
||||
baseAmount() {return player.points}, // Get the current amount of baseResource
|
||||
type: "normal", // normal: cost to gain currency depends on amount gained. static: cost depends on how much you already have
|
||||
exponent: 0.5, // Prestige currency exponent
|
||||
gainMult() { // Calculate the multiplier for main currency from bonuses
|
||||
let mult = new Decimal(1)
|
||||
if (hasUpgrade(this.layer,131))mult=mult.times(10)
|
||||
if (player.i.unlocked)mult=mult.times(player.i.points.plus(1).pow(hasUpgrade("p",235)?6.9420:1))
|
||||
if (hasUpgrade(this.layer,222))mult=mult.times(getBuyableAmount(this.layer,22).plus(1))
|
||||
if (hasUpgrade("p",231)){
|
||||
let asdf = (hasUpgrade("p",132)?player.p.gp.plus(1).pow(new Decimal(1).div(2)):hasUpgrade("p",101)?player.p.gp.plus(1).pow(new Decimal(1).div(3)):hasUpgrade("p",93)?player.p.gp.plus(1).pow(0.2):player.p.gp.plus(1).log10())
|
||||
mult=mult.mul(asdf.plus(1))
|
||||
}
|
||||
if (hasMilestone(this.layer,13))mult=mult.mul(new Decimal(2).plus(layers.p.buyables[33].effect).pow(getBuyableAmount(this.layer,32)))
|
||||
return mult
|
||||
},
|
||||
gainExp() { // Calculate the exponent on main currency from bonuses
|
||||
return new Decimal(1)
|
||||
},
|
||||
row: 0, // Row the layer is in on the tree (0 is the first row)
|
||||
hotkeys: [
|
||||
{key: "p", description: "P: Reset for prestige points", press(){if (layers.p.canReset) resetLayer(this.layer)}},
|
||||
],
|
||||
layerShown(){return true},
|
||||
upgrades:{
|
||||
rows: 999,
|
||||
cols: 5,
|
||||
11:{
|
||||
title: "Gain points",
|
||||
description: "Point generation is increased by 1",
|
||||
cost(){
|
||||
if (hasMilestone(this.layer,2))return new Decimal(1)
|
||||
return new Decimal(1.00001)},
|
||||
unlocked(){return true}
|
||||
},
|
||||
12:{
|
||||
title: "Gain more points",
|
||||
description: "Point generation is singled",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,11)}
|
||||
},
|
||||
13:{
|
||||
title: "Gain more points",
|
||||
description: "Point generation is lined",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,12)}
|
||||
},
|
||||
14:{
|
||||
title: "Gain more points",
|
||||
description: "Point generation is tetrated by 1",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,13)}
|
||||
},
|
||||
21:{
|
||||
title: "Lower prestige requirement",
|
||||
description: "Prestige point requirement is superrooted by 1",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,14)}
|
||||
},
|
||||
22:{
|
||||
title: "Lower prestige requirement more",
|
||||
description: "Prestige point requirement is line rooted",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,21)}
|
||||
},
|
||||
23:{
|
||||
title: "Lower prestige requirement more",
|
||||
description: "Prestige point requirement is wholed",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,22)}
|
||||
},
|
||||
24:{
|
||||
title: "Lower prestige requirement more",
|
||||
description: "Prestige point requirement is decreased by 1",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,23)}
|
||||
},
|
||||
31:{
|
||||
title: "Unlock",
|
||||
description: "Unlock an upgrade",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,24)}
|
||||
},
|
||||
32:{
|
||||
title: "An",
|
||||
description: "Unlock an upgrade",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,31)}
|
||||
},
|
||||
33:{
|
||||
title: "Upgrade",
|
||||
description: "Unlock an upgrade",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,32)}
|
||||
},
|
||||
34:{
|
||||
title: "Increase",
|
||||
description(){return "Add 0.01 to all above upgrades. Currently: +"+format(this.effect)},
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,33)},
|
||||
effect(){
|
||||
let r = (hasUpgrade("p",41)?new Decimal(0.01).times(layers.p.upgrades[41].effect):new Decimal(0.01))
|
||||
r=r.times(new Decimal(1).plus(new Decimal(player[this.layer].challenges[11]).add(1).pow(hasUpgrade(this.layer,121)?1.2:1)))
|
||||
if (hasUpgrade(this.layer,92)) r=r.plus(new Decimal(0.001).times(player[this.layer].g.plus(1)).min(0.05))
|
||||
return r
|
||||
}
|
||||
},
|
||||
41:{
|
||||
title: "Increase again",
|
||||
description(){return "Multiply the previous upgrade by 1.01. Currently: x"+format(this.effect)},
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,34)},
|
||||
effect(){return new Decimal(1.01).pow(hasUpgrade("p",42)?layers.p.upgrades[42].effect:1).times(hasUpgrade("p",63)?2:1)}
|
||||
},
|
||||
42:{
|
||||
title: "Increase again",
|
||||
description(){return "Exponentiate the previous upgrade by 1.01. Currently: ^"+format(this.effect)},
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,41)},
|
||||
effect(){return new Decimal(1.01).tetrate(hasUpgrade("p",43)?layers.p.upgrades[43].effect:1).times(hasUpgrade("p",63)?2:1).times(hasUpgrade("p",64)?2:1)}
|
||||
},
|
||||
43:{
|
||||
title: "Increase again",
|
||||
description(){return "Tetrate the previous upgrade by 1.01. Currently: ^^"+format(this.effect)},
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,42)},
|
||||
effect(){return new Decimal(1.01).pentate(hasUpgrade("p",44)?layers.p.upgrades[44].effect:1).times(hasUpgrade("p",63)?2:1).times(hasUpgrade("p",64)?2:1)}
|
||||
},
|
||||
44:{
|
||||
title: "Increase again",
|
||||
description(){return "Pentate the previous upgrade by 1.01. Currently: ^^^"+format(this.effect)},
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,43)},
|
||||
effect(){return new Decimal(1.01).times(hasUpgrade("p",63)?2:1).times(hasUpgrade("p",64)?2:1)}
|
||||
},
|
||||
51:{
|
||||
title: "Challenging",
|
||||
description: "This upgrade doesn't unlock a challenge",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,44)},
|
||||
},
|
||||
52:{
|
||||
title: "Not challenging",
|
||||
description: "This upgrade doesn't add 1 to the completion limit",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,51)},
|
||||
},
|
||||
53:{
|
||||
title: "Not not challenging",
|
||||
description: "This upgrade doesn't add 1 to the completion limit",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,52)},
|
||||
},
|
||||
54:{
|
||||
title: "(not^3) challenging",
|
||||
description: "Fix the bug where you can't buy upgrades when you have 1 prestige point",
|
||||
cost(){return new Decimal(0.99999)},
|
||||
unlocked(){return hasUpgrade(this.layer,53)},
|
||||
onPurchase(){player.p.points=player.p.points.round()},
|
||||
},
|
||||
61:{
|
||||
title: "(not^4) challenging",
|
||||
description: "Doesn't unlock a second challenge",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,54)&&hasUpgrade(this.layer,53)},
|
||||
},
|
||||
62:{
|
||||
title: "Infinity points",
|
||||
description: "You can now complete Time Dilation 4 more times",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,61)},
|
||||
},
|
||||
63:{
|
||||
title: "Eternity points",
|
||||
description: "Double all fourth row upgrade effects",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,62)},
|
||||
},
|
||||
64:{
|
||||
title: "Reality points",
|
||||
description: "Previous upgrade, but only to the last 3 upgrades",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,63)},
|
||||
},
|
||||
71:{
|
||||
title: "1",
|
||||
description: "Add 1.1 to point gain, but reset all above upgrades",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,64)},
|
||||
onPurchase(){if (!hasMilestone(this.layer,0)) player[this.layer].upgrades=[71]}
|
||||
},
|
||||
72:{
|
||||
title: "2",
|
||||
description: "Multiply point gain by 1.1, but reset all above upgrades",
|
||||
cost(){return new Decimal(2)},
|
||||
unlocked(){return hasUpgrade(this.layer,64)&&hasUpgrade(this.layer, this.id-1)},
|
||||
onPurchase(){if (!hasMilestone(this.layer,1))player[this.layer].upgrades=[71,72]}
|
||||
},
|
||||
73:{
|
||||
title: "3",
|
||||
description: "Raise point gain by ^1.1, but reset all above upgrades",
|
||||
cost(){return new Decimal(4)},
|
||||
unlocked(){return hasUpgrade(this.layer,64)&&hasUpgrade(this.layer, this.id-1)},
|
||||
onPurchase(){if (!hasMilestone(this.layer,1))player[this.layer].upgrades=[71,72,73]}
|
||||
},
|
||||
74:{
|
||||
title: "4",
|
||||
description: "Tetrate point gain by 1.1, but reset all above upgrades",
|
||||
cost(){return new Decimal(8)},
|
||||
unlocked(){return hasUpgrade(this.layer,64)&&hasUpgrade(this.layer, this.id-1)},
|
||||
onPurchase(){if (!hasMilestone(this.layer,2))player[this.layer].upgrades=[71,72,73,74]
|
||||
if (hasMilestone(this.layer,1)&&!hasMilestone(this.layer,2)) {player[this.layer].upgrades=[11,12,13,14,21,22,23,24,71,72,73,74]}}
|
||||
},
|
||||
81:{
|
||||
title: "5",
|
||||
description: "Generator efficiency is increased by 2",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,74)&&(player[this.layer].buyables[12].gt(0)||player[this.layer].buyables[21].gt(0))},
|
||||
},
|
||||
82:{
|
||||
title: "6",
|
||||
description: "Unlock another way to buy generators",
|
||||
cost(){return new Decimal(1)},
|
||||
unlocked(){return hasUpgrade(this.layer,81)&&(player[this.layer].buyables[12].gt(0)||player[this.layer].buyables[21].gt(0))},
|
||||
},
|
||||
83:{
|
||||
title: "7",
|
||||
description: "Generator efficiency is boosted by prestige points",
|
||||
cost(){return new Decimal(3)},
|
||||
unlocked(){return hasUpgrade(this.layer,82)},
|
||||
},
|
||||
84:{
|
||||
title: "8",
|
||||
description: "You can complete <b>Point</b> one more time",
|
||||
cost(){return new Decimal(3)},
|
||||
unlocked(){return hasUpgrade(this.layer,83)},
|
||||
},
|
||||
91:{
|
||||
title: "9",
|
||||
description: "New Challenge Time",
|
||||
cost(){return new Decimal(20)},
|
||||
unlocked(){return hasUpgrade(this.layer,84)&&new Decimal(player[this.layer].challenges[12]).gte(3)},
|
||||
},
|
||||
92:{
|
||||
title: "10",
|
||||
description: "Each of the first 50 generators adds 0.001 to <b>Increase</b>",
|
||||
cost(){return new Decimal(5)},
|
||||
unlocked(){return hasUpgrade(this.layer,91)&&hasChallenge(this.layer,21)},
|
||||
},
|
||||
93:{
|
||||
title: "11",
|
||||
description: "Change the tree trunk in generator effect to a hypertessaract root",
|
||||
cost(){return new Decimal(7)},
|
||||
unlocked(){return hasUpgrade(this.layer,92)},
|
||||
},
|
||||
94:{
|
||||
title: "12",
|
||||
description: "Unlock a clickable in generators",
|
||||
cost(){return new Decimal(50)},
|
||||
unlocked(){return hasUpgrade(this.layer,93)},
|
||||
},
|
||||
101:{
|
||||
title: "10th row????",
|
||||
description: "Decrease the dimensions of <b>11</b> by 2",
|
||||
cost(){return new Decimal(10)},
|
||||
unlocked(){return hasUpgrade(this.layer,94)},
|
||||
},
|
||||
102:{
|
||||
title: "2 Tree Trunks",
|
||||
description: "Double log of generator points adds to generator efficiency",
|
||||
cost(){return new Decimal(25)},
|
||||
unlocked(){return hasUpgrade(this.layer,101)},
|
||||
},
|
||||
103:{
|
||||
title: "(not^5) challenging",
|
||||
description: "Unlock the last challenge",
|
||||
cost(){return new Decimal(103)},
|
||||
unlocked(){return hasUpgrade(this.layer,102)},
|
||||
},
|
||||
104:{
|
||||
title: "2 layers tree",
|
||||
description: "Prestige points boost points, and unlock another tab",
|
||||
cost(){return new Decimal(100)},
|
||||
unlocked(){return hasUpgrade(this.layer,103)&&hasChallenge(this.layer,22)},
|
||||
},
|
||||
111:{
|
||||
title: "not (hardcapped)",
|
||||
description: "Remove the generator clickable hardcap, and you can only pick one upgrade on each row below this",
|
||||
cost(){return new Decimal(110)},
|
||||
unlocked(){return hasUpgrade(this.layer,104)&&hasMilestone(this.layer,6)},
|
||||
},
|
||||
112:{
|
||||
title: "Respec button",
|
||||
description: "Respec all lower upgrades, but you don't get points back",
|
||||
cost(){return new Decimal(100)},
|
||||
unlocked(){return hasUpgrade(this.layer,111)&&(hasUpgrade(this.layer,121)||hasUpgrade(this.layer,122))&&!hasMilestone("i",4)},
|
||||
onPurchase(){
|
||||
player.p.upgrades=player.p.upgrades.filter(function x(i){return i<112})
|
||||
}
|
||||
},
|
||||
121:{
|
||||
title: "Timers",
|
||||
description: "Raise the <b>Time Dilation</b> reward effect to the 1.2",
|
||||
cost(){return new Decimal(500)},
|
||||
unlocked(){return hasUpgrade(this.layer,111)&&(!hasUpgrade(this.layer,122)||hasMilestone(this.layer,7))},
|
||||
},
|
||||
122:{
|
||||
title: "Generators",
|
||||
description: "Decrease the first generator buyable cost scaling base by 2",
|
||||
cost(){return new Decimal(500)},
|
||||
unlocked(){return hasUpgrade(this.layer,111)&&(!hasUpgrade(this.layer,121)||hasMilestone(this.layer,7))},
|
||||
},
|
||||
131:{
|
||||
title: "Prestige",
|
||||
description: "Gain 10x more prestige points",
|
||||
cost(){return new Decimal(5000)},
|
||||
unlocked(){return (hasUpgrade(this.layer,121)||hasUpgrade(this.layer,122))&&(!hasUpgrade(this.layer,132)||hasMilestone(this.layer,7))},
|
||||
},
|
||||
132:{
|
||||
title: "One and a half",
|
||||
description: "Raise generator effect to the 1.5",
|
||||
cost(){return new Decimal(5000)},
|
||||
unlocked(){return (hasUpgrade(this.layer,121)||hasUpgrade(this.layer,122))&&(!hasUpgrade(this.layer,131)||hasMilestone(this.layer,7))},
|
||||
},
|
||||
|
||||
141:{
|
||||
title: "Active",
|
||||
description: "Multiply generator efficiency now increases by 1, but it doesn't automatically click.",
|
||||
cost(){return new Decimal(50000)},
|
||||
unlocked(){return (hasUpgrade(this.layer,131)||hasUpgrade(this.layer,132))&&((!hasUpgrade(this.layer,142)&&!hasUpgrade(this.layer,143))||hasMilestone("i",4))},
|
||||
},
|
||||
142:{
|
||||
title: "Passive",
|
||||
description: "Gain 5x more points",
|
||||
cost(){return new Decimal(50000)},
|
||||
unlocked(){return (hasUpgrade(this.layer,131)||hasUpgrade(this.layer,132))&&((!hasUpgrade(this.layer,141)&&!hasUpgrade(this.layer,143))||hasMilestone("i",4))},
|
||||
},
|
||||
143:{
|
||||
title: "Idle",
|
||||
description: "Hours played multiply generator power",
|
||||
cost(){return new Decimal(50000)},
|
||||
unlocked(){return (hasUpgrade(this.layer,131)||hasUpgrade(this.layer,132))&&((!hasUpgrade(this.layer,142)&&!hasUpgrade(this.layer,141))||hasMilestone("i",4))},
|
||||
},
|
||||
211:{
|
||||
title: "Prestige",
|
||||
description: "Pointy points multiply points",
|
||||
cost(){return new Decimal(1)},
|
||||
canAfford(){return getBuyableAmount(this.layer,22).gte(this.cost)},
|
||||
pay(){setBuyableAmount(this.layer,22,getBuyableAmount(this.layer,22).sub(this.cost))},
|
||||
unlocked(){return (hasMilestone("i",5)&&layers.p.activeSubtab!="Upgrades")},
|
||||
},
|
||||
212:{
|
||||
title: "Pointy",
|
||||
description: "Pointy prestige points reduce the cost scaling of pointy points",
|
||||
cost(){return new Decimal(2)},
|
||||
canAfford(){return getBuyableAmount(this.layer,22).gte(this.cost)},
|
||||
pay(){setBuyableAmount(this.layer,22,getBuyableAmount(this.layer,22).sub(this.cost))},
|
||||
unlocked(){return (hasMilestone("i",5)&&layers.p.activeSubtab!="Upgrades"&&hasUpgrade(this.layer,211))},
|
||||
},
|
||||
213:{
|
||||
title: "Time",
|
||||
description: "Generator power also multiplies point gain",
|
||||
cost(){return new Decimal(6)},
|
||||
canAfford(){return getBuyableAmount(this.layer,22).gte(this.cost)},
|
||||
pay(){setBuyableAmount(this.layer,22,getBuyableAmount(this.layer,22).sub(this.cost))},
|
||||
unlocked(){return (hasMilestone("i",5)&&layers.p.activeSubtab!="Upgrades"&&hasUpgrade(this.layer,212))},
|
||||
},
|
||||
214:{
|
||||
title: "^0",
|
||||
description: "Further reduce the pointy point scaling",
|
||||
cost(){return new Decimal(11)},
|
||||
canAfford(){return getBuyableAmount(this.layer,22).gte(this.cost)},
|
||||
pay(){setBuyableAmount(this.layer,22,getBuyableAmount(this.layer,22).sub(this.cost))},
|
||||
unlocked(){return (hasMilestone("i",5)&&layers.p.activeSubtab!="Upgrades"&&hasUpgrade(this.layer,213))},
|
||||
},
|
||||
215:{
|
||||
title: "bulk",
|
||||
description: "Auto-pointy points now buys 10 per tick",
|
||||
cost(){return new Decimal(27)},
|
||||
canAfford(){return getBuyableAmount(this.layer,22).gte(this.cost)},
|
||||
pay(){setBuyableAmount(this.layer,22,getBuyableAmount(this.layer,22).sub(this.cost))},
|
||||
unlocked(){return (hasMilestone("i",5)&&layers.p.activeSubtab!="Upgrades"&&hasUpgrade(this.layer,214))},
|
||||
},
|
||||
221:{
|
||||
title: "^-1",
|
||||
description: "^0 is even more powerful",
|
||||
cost(){return new Decimal(28)},
|
||||
canAfford(){return getBuyableAmount(this.layer,22).gte(this.cost)},
|
||||
pay(){setBuyableAmount(this.layer,22,getBuyableAmount(this.layer,22).sub(this.cost))},
|
||||
unlocked(){return (hasMilestone("i",5)&&layers.p.activeSubtab!="Upgrades"&&hasUpgrade(this.layer,215))},
|
||||
},
|
||||
222:{
|
||||
title: "???",
|
||||
description: "square <b>bulk</b> and pointy prestige points multiply prestige points",
|
||||
cost(){return new Decimal(90)},
|
||||
canAfford(){return getBuyableAmount(this.layer,22).gte(this.cost)},
|
||||
pay(){setBuyableAmount(this.layer,22,getBuyableAmount(this.layer,22).sub(this.cost))},
|
||||
unlocked(){return (hasMilestone("i",5)&&layers.p.activeSubtab!="Upgrades"&&hasUpgrade(this.layer,221))},
|
||||
},
|
||||
223:{
|
||||
title: "more automation",
|
||||
description: "Automatically gain pointy prestige points",
|
||||
cost(){return new Decimal(96)},
|
||||
canAfford(){return getBuyableAmount(this.layer,22).gte(this.cost)},
|
||||
pay(){setBuyableAmount(this.layer,22,getBuyableAmount(this.layer,22).sub(this.cost))},
|
||||
unlocked(){return (hasMilestone("i",5)&&layers.p.activeSubtab!="Upgrades"&&hasUpgrade(this.layer,222))},
|
||||
},
|
||||
224:{
|
||||
title: "Generation",
|
||||
description: "Generator costs are divided by generator effect",
|
||||
cost(){return new Decimal(100)},
|
||||
canAfford(){return getBuyableAmount(this.layer,22).gte(this.cost)},
|
||||
pay(){setBuyableAmount(this.layer,22,getBuyableAmount(this.layer,22).sub(this.cost))},
|
||||
unlocked(){return (hasMilestone("i",5)&&layers.p.activeSubtab!="Upgrades"&&hasUpgrade(this.layer,223))},
|
||||
},
|
||||
225:{
|
||||
title: "Boosters",
|
||||
description: "Unlock boosters (next update)",
|
||||
cost(){return new Decimal(135)},
|
||||
canAfford(){return getBuyableAmount(this.layer,22).gte(this.cost)},
|
||||
pay(){setBuyableAmount(this.layer,22,getBuyableAmount(this.layer,22).sub(this.cost))},
|
||||
unlocked(){return (hasMilestone("i",5)&&layers.p.activeSubtab!="Upgrades"&&hasUpgrade(this.layer,224))},
|
||||
},
|
||||
231:{
|
||||
title: "Blue",
|
||||
description: "The generator effect also affects prestige points",
|
||||
cost(){return new Decimal(4)},
|
||||
canAfford(){return getBuyableAmount(this.layer,23).gte(this.cost)},
|
||||
pay(){setBuyableAmount(this.layer,23,getBuyableAmount(this.layer,23).sub(this.cost))},
|
||||
unlocked(){return (layers.p.activeSubtab!="Upgrades"&&hasMilestone(this.layer,11))},
|
||||
currencyDisplayName: "pointy boosters"
|
||||
},
|
||||
232:{
|
||||
title: "Red",
|
||||
description: "Unlock a third way to buy generators",
|
||||
cost(){return new Decimal(5)},
|
||||
canAfford(){return getBuyableAmount(this.layer,23).gte(this.cost)},
|
||||
pay(){setBuyableAmount(this.layer,23,getBuyableAmount(this.layer,23).sub(this.cost))},
|
||||
unlocked(){return (layers.p.activeSubtab!="Upgrades"&&hasMilestone(this.layer,12))},
|
||||
currencyDisplayName: "pointy boosters"
|
||||
},
|
||||
233:{
|
||||
title: "Green",
|
||||
description: "Prestige points do not reset your pointy points and boosters don't reset generators",
|
||||
cost(){return new Decimal(5)},
|
||||
canAfford(){return getBuyableAmount(this.layer,23).gte(this.cost)},
|
||||
pay(){setBuyableAmount(this.layer,23,getBuyableAmount(this.layer,23).sub(this.cost))},
|
||||
unlocked(){return (layers.p.activeSubtab!="Upgrades"&&hasMilestone(this.layer,12))},
|
||||
currencyDisplayName: "pointy boosters"
|
||||
},
|
||||
234:{
|
||||
title: "Yellow",
|
||||
description: "Divide the cost of the third generator buyable based on boosters",
|
||||
cost(){return new Decimal(6)},
|
||||
canAfford(){return getBuyableAmount(this.layer,23).gte(this.cost)},
|
||||
pay(){setBuyableAmount(this.layer,23,getBuyableAmount(this.layer,23).sub(this.cost))},
|
||||
unlocked(){return (layers.p.activeSubtab!="Upgrades"&&hasMilestone(this.layer,12))},
|
||||
currencyDisplayName: "pointy boosters"
|
||||
},
|
||||
235:{
|
||||
title: "Orange",
|
||||
description: "Raise the Infinity effect to the 6.9420th power",
|
||||
cost(){return new Decimal(8)},
|
||||
canAfford(){return getBuyableAmount(this.layer,23).gte(this.cost)},
|
||||
pay(){setBuyableAmount(this.layer,23,getBuyableAmount(this.layer,23).sub(this.cost))},
|
||||
unlocked(){return (layers.p.activeSubtab!="Upgrades"&&hasMilestone(this.layer,12))},
|
||||
currencyDisplayName: "pointy boosters"
|
||||
},
|
||||
},
|
||||
|
||||
clickables: {
|
||||
rows: 1,
|
||||
cols: 1,
|
||||
11: {
|
||||
|
||||
display() {return "Multiply generator efficiency by "+format(player.p.cmult)+((player.p.cmult.min(100).eq(100)&&!hasUpgrade(this.layer,111))?" (hardcapped)":"")},
|
||||
unlocked(){return hasUpgrade("p",94)},
|
||||
click(){player.p.cmult=player.p.cmult.plus(hasUpgrade("p",141)?1:0.01)
|
||||
if (!hasUpgrade(this.layer,111))player.p.cmult=player.p.cmult.min(100)
|
||||
},
|
||||
canClick(){return player.p.cmult.lt(100)||hasUpgrade(this.layer,111)},
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
challenges:{
|
||||
rows: 99,
|
||||
cols: 2,
|
||||
11:{
|
||||
name: "Time dilation",
|
||||
challengeDescription(){return "Point gain exponent is raised to the ^0.75"},
|
||||
goal(){return new Decimal(100).times(new Decimal(10).pow(new Decimal(player[this.layer].challenges[this.id]).times(new Decimal(1).sub(new Decimal(layers[this.layer].challenges[12].effect).div(100))).pow(2)))},
|
||||
rewardDescription(){return "You have completed this challenge "+player[this.layer].challenges[this.id]+"/"+this.completionLimit+" times. Multiply <b>Increase</b>'s effect by challenge completions+1. Currently: x"+format(new Decimal(player[this.layer].challenges[this.id]).add(1).pow(hasUpgrade(this.layer,121)?1.2:1))},
|
||||
unlocked(){return hasUpgrade("p",51)||hasChallenge(this.layer,this.id)},
|
||||
completionLimit(){
|
||||
if (hasUpgrade("p",62))return 7
|
||||
if (hasUpgrade("p",53))return 3
|
||||
if (hasUpgrade("p",52))return 2
|
||||
return 1
|
||||
|
||||
}
|
||||
},
|
||||
12:{
|
||||
name: "Point",
|
||||
challengeDescription: "Points are pointed",
|
||||
goal(){return new Decimal(100)},
|
||||
rewardDescription(){return "You have completed this challenge "+player[this.layer].challenges[this.id]+"/"+this.completionLimit+" times, making previous challenge goal scale "+(layers[this.layer].challenges[this.id].effect)+"% slower."},
|
||||
unlocked(){return hasUpgrade("p",61)||hasChallenge(this.layer,this.id)},
|
||||
effect(){
|
||||
if (!hasChallenge(this.layer,this.id)) return 0
|
||||
if (player[this.layer].challenges[this.id]==1) return 50
|
||||
if (player[this.layer].challenges[this.id]==2) return 60
|
||||
if (player[this.layer].challenges[this.id]==3) return 70
|
||||
},
|
||||
completionLimit(){
|
||||
let l=new Decimal(1)
|
||||
if (hasUpgrade("p",84)) l=l.plus(1)
|
||||
if (hasMilestone("p",3))l=l.plus(1)
|
||||
return l
|
||||
|
||||
}
|
||||
},
|
||||
21:{
|
||||
name: "Time Points",
|
||||
challengeDescription: "You are stuck in all above challenges",
|
||||
goal(){return new Decimal(308.25)},
|
||||
rewardDescription(){return "Lower the first generator buyable cost base by 6"},
|
||||
unlocked(){return hasUpgrade("p",91)||hasChallenge(this.layer,this.id)},
|
||||
},
|
||||
22:{
|
||||
name: "Last Challenge",
|
||||
challengeDescription: "Generator points do nothing",
|
||||
goal(){return new Decimal(9999)},
|
||||
rewardDescription(){return "Autoclick the clickable and reduce <b>2 Tree Trunks</b> by 1"},
|
||||
unlocked(){return hasUpgrade("p",103)||hasChallenge(this.layer,this.id)},
|
||||
}
|
||||
},
|
||||
buyables: {
|
||||
rows: 99,
|
||||
cols: 4,
|
||||
11: {
|
||||
cost() { return new Decimal(0)},
|
||||
display() { return "Reset all upgrades and challenges, but get a boost. You have reset "+getBuyableAmount(this.layer,this.id)+" times.<br>"+(getBuyableAmount(this.layer,this.id).eq(6)?"You can't buy more than 6 boosts!":"You need all upgrades to reset.") },
|
||||
canAfford() { return (player[this.layer].points.gte(this.cost)&&hasUpgrade(this.layer,74)&&hasUpgrade(this.layer,64))&&getBuyableAmount(this.layer,this.id).lt(6) },
|
||||
buy() {
|
||||
player[this.layer].points = player[this.layer].points.sub(this.cost)
|
||||
setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1))
|
||||
player[this.layer].points=new Decimal(0)
|
||||
player[this.layer].upgrades=[]
|
||||
if (hasMilestone(this.layer,1))player[this.layer].upgrades=[11,12,13,14,21,22,23,24]
|
||||
if (hasMilestone(this.layer,3))player[this.layer].upgrades=[11,12,13,14,21,22,23,24,31,32,33,34,41,42,43,44,51,52,53,54,61,62,63,64]
|
||||
if (!hasMilestone(this.layer,2)){
|
||||
for (let c in layers[this.layer].challenges){
|
||||
player[this.layer].challenges[c]=new Decimal(0)
|
||||
}
|
||||
}
|
||||
},
|
||||
unlocked(){return (hasUpgrade(this.layer,74)&&hasUpgrade(this.layer,64))||hasMilestone(this.layer,0)}
|
||||
},
|
||||
12: {
|
||||
cost() { return new Decimal(1).times(new Decimal(hasChallenge(this.layer,21)?4:10).sub(hasUpgrade(this.layer,122)?2:0).pow(player.p.buyables[this.id])).div(hasUpgrade(this.layer,224)?(hasUpgrade("p",132)?player.p.gp.plus(1).pow(new Decimal(1).div(2)):hasUpgrade("p",101)?player.p.gp.plus(1).pow(new Decimal(1).div(3)):hasUpgrade("p",93)?player.p.gp.plus(1).pow(0.2):player.p.gp.plus(1).log10()):1)},
|
||||
display() { return "Buy a generator for "+format(this.cost)+" points" },
|
||||
canAfford() { return (player.points.gte(this.cost)&&hasMilestone(this.layer,5)) },
|
||||
buy() {
|
||||
if (!hasMilestone("p",13))player.points = player.points.sub(this.cost)
|
||||
setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1))
|
||||
player[this.layer].g=player[this.layer].g.plus(1)
|
||||
},
|
||||
unlocked(){return (hasMilestone(this.layer,5))}
|
||||
},
|
||||
13: {
|
||||
cost() { return new Decimal(1).times(new Decimal(2).pow(player.p.buyables[this.id])).div(hasUpgrade(this.layer,224)?(hasUpgrade("p",132)?player.p.gp.plus(1).pow(new Decimal(1).div(2)):hasUpgrade("p",101)?player.p.gp.plus(1).pow(new Decimal(1).div(3)):hasUpgrade("p",93)?player.p.gp.plus(1).pow(0.2):player.p.gp.plus(1).log10()):1)},
|
||||
display() { return "Buy a generator for "+format(this.cost)+" prestige points" },
|
||||
canAfford() { return (player.p.points.gte(this.cost)&&hasUpgrade("p",82)) },
|
||||
buy() {
|
||||
if (!hasMilestone("p",13))player.p.points = player.p.points.sub(this.cost)
|
||||
setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1))
|
||||
player[this.layer].g=player[this.layer].g.plus(1)
|
||||
},
|
||||
unlocked(){return (hasUpgrade(this.layer,82))}
|
||||
},
|
||||
14: {
|
||||
cost() { return new Decimal(900).mul(new Decimal(1.01).pow(getBuyableAmount(this.layer,this.id))).round().div(hasUpgrade(this.layer,234)?getBuyableAmount(this.layer,23).pow(0.3).plus(1):1)},
|
||||
display() { return "Buy a generator for "+format(this.cost)+" Infinity points" },
|
||||
canAfford() { return (player.i.points.gte(this.cost)&&hasUpgrade("p",232)) },
|
||||
buy() {
|
||||
if (!hasMilestone("p",13))player.i.points = player.i.points.sub(this.cost).round()
|
||||
setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1))
|
||||
player[this.layer].g=player[this.layer].g.plus(1)
|
||||
},
|
||||
unlocked(){return (hasUpgrade(this.layer,232))}
|
||||
},
|
||||
21: {
|
||||
cost() { return new Decimal(20).plus(getBuyableAmount(this.layer, this.id).pow(new Decimal(2).sub(new Decimal(hasUpgrade(this.layer,221)?0.9:hasUpgrade(this.layer,214)?0.6:0.3).times(hasUpgrade(this.layer,212)?(new Decimal(1).sub(new Decimal(0.75).pow(getBuyableAmount(this.layer,22)))):0))))},
|
||||
display() { return "Reset your generators for +1 pointy point! Cost: "+format(this.cost)+" Generators" },
|
||||
canAfford() { return (player.p.g.gte(this.cost)&&hasUpgrade("p",104)) },
|
||||
buy() {
|
||||
if (!hasMilestone("i",1))player.p.g = new Decimal(0)
|
||||
setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1))
|
||||
if (!hasMilestone("i",1))setBuyableAmount(this.layer,12, new Decimal(0))
|
||||
if (!hasMilestone("i",1))setBuyableAmount(this.layer,13, new Decimal(0))
|
||||
if (!hasMilestone("i",1))player.p.gp=new Decimal(0)
|
||||
},
|
||||
unlocked(){return (hasUpgrade(this.layer,104))}
|
||||
},
|
||||
22: {
|
||||
cost() { return new Decimal(8).plus(getBuyableAmount(this.layer,this.id))},
|
||||
display() { return "Gain a pointy prestige point. Cost: "+format(this.cost)+" Pointy Points" },
|
||||
canAfford() { return (getBuyableAmount(this.layer,21).gte(this.cost)&&(hasMilestone("i",5))) },
|
||||
buy() {
|
||||
if (!hasUpgrade(this.layer,233))setBuyableAmount(this.layer,21, getBuyableAmount(this.layer,21).sub(this.cost))
|
||||
setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1))
|
||||
},
|
||||
unlocked(){return (hasMilestone("i",5))}
|
||||
},
|
||||
23: {
|
||||
cost() { return new Decimal(124).plus(getBuyableAmount(this.layer,this.id).times(2).pow(2))},
|
||||
display() { return "Gain a booster. Cost: "+format(this.cost)+" Pointy Points" },
|
||||
canAfford() { return (getBuyableAmount(this.layer,21).gte(this.cost)&&(hasMilestone("i",5))) },
|
||||
buy() {
|
||||
if (!hasMilestone(this.layer,15))setBuyableAmount(this.layer,21, getBuyableAmount(this.layer,21).sub(this.cost))
|
||||
setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1))
|
||||
if (!hasMilestone(this.layer,15)){
|
||||
if (!hasMilestone(this.layer,12)){player.p.upgrades=player.p.upgrades.filter((x)=>{return (x<200||x>230)})
|
||||
if (hasMilestone(this.layer,11)){player.p.upgrades.push(215);player.p.upgrades.push(225);player.p.upgrades.push(223);player.p.upgrades.push(222)}}
|
||||
setBuyableAmount("p",21,new Decimal(0))
|
||||
setBuyableAmount("p",22,new Decimal(0))
|
||||
if (!hasUpgrade("p",233)){
|
||||
setBuyableAmount("p",12,new Decimal(0))
|
||||
setBuyableAmount("p",13,new Decimal(0))
|
||||
setBuyableAmount("p",14,new Decimal(0))
|
||||
|
||||
player.p.g = new Decimal(0)}
|
||||
player.p.gp=new Decimal(0)}
|
||||
},
|
||||
unlocked(){return (hasUpgrade("p",225)||getBuyableAmount("p",23).gt(0))}
|
||||
},
|
||||
31: {
|
||||
cost() { return new Decimal(1e93).times(new Decimal(1.5).pow(getBuyableAmount(this.layer,this.id))).times(new Decimal(1.1).pow(getBuyableAmount(this.layer,this.id).pow(2)))},
|
||||
effect(){return new Decimal(2).plus(layers.p.buyables[33].effect).pow(getBuyableAmount(this.layer,this.id).plus(layers.p.buyables[51].effect))},
|
||||
display() { return "Double point gain. \nCurrently: x"+format(this.effect)+"\nCost: "+format(this.cost)+" Prestige points" },
|
||||
canAfford() { return (player.p.points.gte(this.cost)&&(hasMilestone("p",13))) },
|
||||
buy() {
|
||||
player.p.points=player.p.points.sub(this.cost)
|
||||
setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1))
|
||||
},
|
||||
unlocked(){return (hasMilestone("p",13))}
|
||||
},
|
||||
32: {
|
||||
cost() { return new Decimal(1e95).times(new Decimal(2).pow(getBuyableAmount(this.layer,this.id))).times(new Decimal(1.01).pow(getBuyableAmount(this.layer,this.id).pow(2)))},
|
||||
display() { return "Double prestige point gain. \nCurrently: x"+format(new Decimal(2).plus(layers.p.buyables[33].effect).pow(getBuyableAmount(this.layer,this.id)))+"\nCost: "+format(this.cost)+" Prestige points" },
|
||||
canAfford() { return (player.p.points.gte(this.cost)&&(hasMilestone("p",13))) },
|
||||
buy() {
|
||||
player.p.points=player.p.points.sub(this.cost)
|
||||
setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1))
|
||||
},
|
||||
unlocked(){return (hasMilestone("p",13)&&getBuyableAmount(this.layer,31).gte(5))}
|
||||
},
|
||||
33: {
|
||||
cost() { return new Decimal(1e100).times(new Decimal(10).pow(getBuyableAmount(this.layer,this.id))).times(new Decimal(1.01).pow(getBuyableAmount(this.layer,this.id).pow(2)))},
|
||||
effect(){return new Decimal(0.01).mul(getBuyableAmount(this.layer,this.id)).times(layers.p.buyables[43].effect)},
|
||||
display() { return "Add 0.01 to the previous 2 buyable bases. \nCurrently: +"+format(this.effect)+"\nCost: "+format(this.cost)+" Prestige points" },
|
||||
canAfford() { return (player.p.points.gte(this.cost)&&(hasMilestone("p",13))) },
|
||||
buy() {
|
||||
player.p.points=player.p.points.sub(this.cost)
|
||||
setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1))
|
||||
},
|
||||
unlocked(){return (hasMilestone("p",13)&&(getBuyableAmount(this.layer,this.id).gt(0)||player.p.points.gte(1e100)))}
|
||||
},
|
||||
41: {
|
||||
cost() { return new Decimal(1e110).times(new Decimal(10).pow(getBuyableAmount(this.layer,this.id))).times(new Decimal(10).pow(getBuyableAmount(this.layer,this.id).pow(2)))},
|
||||
effect(){return new Decimal(0.01).mul(getBuyableAmount(this.layer,this.id).plus(layers.p.buyables[51].effect))},
|
||||
display() { return "Add 0.01 to the booster effect base. \nCurrently: +"+format(this.effect)+"\nCost: "+format(this.cost)+" Prestige points" },
|
||||
canAfford() { return (player.p.points.gte(this.cost)&&(hasMilestone("p",13))) },
|
||||
buy() {
|
||||
player.p.points=player.p.points.sub(this.cost)
|
||||
setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1))
|
||||
},
|
||||
unlocked(){return (hasMilestone("p",13)&&(getBuyableAmount(this.layer,this.id).gt(0)||player.p.points.gte(1e110)))}
|
||||
},
|
||||
42: {
|
||||
cost() { let c = new Decimal(1e270).times(new Decimal(2).pow(getBuyableAmount(this.layer,this.id))).times(new Decimal(1.01).pow(getBuyableAmount(this.layer,this.id).pow(2)))
|
||||
|
||||
return c
|
||||
},
|
||||
effect(){
|
||||
let f= new Decimal(1.001).pow(getBuyableAmount(this.layer,this.id))
|
||||
if (f.gte(1.1))f=f.pow(0.8).times(new Decimal(1.1).pow(0.2))
|
||||
if (f.gte(1.35))f=f.pow(0.5).times(new Decimal(1.35).pow(0.5))
|
||||
if (f.gte(3))f=new Decimal(3)
|
||||
return f
|
||||
},
|
||||
display() { return "Raise point gain to the 1.001 \nCurrently: ^"+format(this.effect)+(this.effect.eq(3)?"(hardcapped)":"")+"\nCost: "+format(this.cost)+" Prestige points" },
|
||||
canAfford() { return (player.p.points.gte(this.cost)&&(hasMilestone("p",13)))&&this.effect.lt(3) },
|
||||
buy() {
|
||||
player.p.points=player.p.points.sub(this.cost)
|
||||
setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1))
|
||||
},
|
||||
unlocked(){return (hasMilestone("p",13)&&(getBuyableAmount(this.layer,this.id).gt(0)||player.p.points.gte(1e270)))}
|
||||
},
|
||||
43: {
|
||||
cost() { return new Decimal("1e375").times(new Decimal(10).pow(getBuyableAmount(this.layer,this.id))).times(new Decimal(10).pow(getBuyableAmount(this.layer,this.id).pow(2)))},
|
||||
effect(){return new Decimal(0.01).mul(getBuyableAmount(this.layer,this.id)).plus(1)},
|
||||
display() { return "Multiply the above buyable effect. \nCurrently: *"+format(this.effect)+"\nCost: "+format(this.cost)+" Prestige points" },
|
||||
canAfford() { return (player.p.points.gte(this.cost)&&(hasMilestone("p",13))) },
|
||||
buy() {
|
||||
player.p.points=player.p.points.sub(this.cost)
|
||||
setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1))
|
||||
},
|
||||
unlocked(){return (hasMilestone("p",13)&&(getBuyableAmount(this.layer,this.id).gt(0)||player.p.points.gte("1e375")))}
|
||||
},
|
||||
51: {
|
||||
cost() { return new Decimal("1e1740").times(new Decimal(10).pow(getBuyableAmount(this.layer,this.id))).times(new Decimal(1e10).pow(getBuyableAmount(this.layer,this.id).pow(2)))},
|
||||
effect(){return getBuyableAmount(this.layer,this.id).pow(0.55)},
|
||||
display() { return "Add free levels to the above 2 buyables \nCurrently: "+format(this.effect)+"\nCost: "+format(this.cost)+" Prestige points" },
|
||||
canAfford() { return (player.p.points.gte(this.cost)&&(hasMilestone("p",13))) },
|
||||
buy() {
|
||||
player.p.points=player.p.points.sub(this.cost)
|
||||
setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1))
|
||||
},
|
||||
unlocked(){return (hasMilestone("p",15)&&(getBuyableAmount(this.layer,this.id).gt(0)||player.p.points.gte("1e1700")))}
|
||||
},
|
||||
},
|
||||
milestones: {
|
||||
0: {
|
||||
requirementDisplay: "1 reset",
|
||||
effectDisplay: "Add 0.01 to base point gain and prestige requirement, and <b>1</b> doesn't reset upgrades",
|
||||
done() { return getBuyableAmount("p",11).gte(1) },
|
||||
unlocked(){return layers.p.activeSubtab!="Pointy points"}
|
||||
},
|
||||
1: {
|
||||
requirementDisplay: "2 resets",
|
||||
effectDisplay: "<div><b>2</b> and <b>3</b> don't reset upgrades, and start with the first 8 upgrades on reset</div>",
|
||||
done() { return getBuyableAmount("p",11).gte(2) },
|
||||
unlocked(){return hasMilestone(this.layer,this.id-1)&&layers.p.activeSubtab!="Pointy points"}
|
||||
},
|
||||
2: {
|
||||
requirementDisplay: "3 resets",
|
||||
effectDisplay: "<div><b>4</b> doesn't reset upgrades, and permanently fix the bug where you can't buy upgrades when you have 1 prestige point</div>",
|
||||
done() { return getBuyableAmount("p",11).gte(3) },
|
||||
unlocked(){return hasMilestone(this.layer,this.id-1)&&layers.p.activeSubtab!="Pointy points"}
|
||||
},
|
||||
3: {
|
||||
requirementDisplay: "4 resets",
|
||||
effectDisplay: "Don't reset challenges, add 1 to <b>Point</b> maximum completions, and start with 24 upgrades",
|
||||
done() { return getBuyableAmount("p",11).gte(4) },
|
||||
unlocked(){return hasMilestone(this.layer,this.id-1)&&layers.p.activeSubtab!="Pointy points"}
|
||||
},
|
||||
4: {
|
||||
requirementDisplay: "5 resets",
|
||||
effectDisplay: "Each useless upgrade adds 0.1 to base point gain",
|
||||
done() { return getBuyableAmount("p",11).gte(5) },
|
||||
unlocked(){return hasMilestone(this.layer,this.id-1)&&layers.p.activeSubtab!="Pointy points"}
|
||||
},
|
||||
5: {
|
||||
requirementDisplay: "6 resets",
|
||||
effectDisplay: "Unlock something",
|
||||
done() { return getBuyableAmount("p",11).gte(6) },
|
||||
unlocked(){return hasMilestone(this.layer,this.id-1)&&layers.p.activeSubtab!="Pointy points"}
|
||||
},
|
||||
6: {
|
||||
requirementDisplay: "1 pointy point",
|
||||
effectDisplay: "Unlock the upgrade tree",
|
||||
done() { return getBuyableAmount("p",21).gte(1) },
|
||||
unlocked(){return hasMilestone(this.layer,this.id-1)&&(hasUpgrade(this.layer,104)||player.i.unlocked)&&layers.p.activeSubtab!="Pointy points"}
|
||||
},
|
||||
7: {
|
||||
requirementDisplay: "7 pointy points",
|
||||
effectDisplay: "You can now buy both first and second row upgrade tree upgrades",
|
||||
done() { return getBuyableAmount("p",21).gte(7) },
|
||||
unlocked(){return hasMilestone(this.layer,this.id-1)&&(hasUpgrade(this.layer,111)||player.i.unlocked)&&layers.p.activeSubtab!="Pointy points"}
|
||||
},
|
||||
8: {
|
||||
requirementDisplay: "8 pointy points",
|
||||
effectDisplay: "Unlock another layer",
|
||||
done() { return getBuyableAmount("p",21).gte(8) },
|
||||
unlocked(){return hasMilestone(this.layer,this.id-1)&&(hasUpgrade(this.layer,141)||hasUpgrade(this.layer,143)||hasUpgrade(this.layer,142)||player.i.unlocked)&&layers.p.activeSubtab!="Pointy points"}
|
||||
},
|
||||
11: {
|
||||
requirementDisplay: "3 boosters",
|
||||
effectDisplay: "Keep automation on booster reset",
|
||||
done() { return getBuyableAmount("p",23).gte(3) },
|
||||
unlocked(){return (getBuyableAmount("p",23).gt(0)||hasMilestone(this.layer,this.id))}
|
||||
},
|
||||
12: {
|
||||
requirementDisplay: "5 boosters",
|
||||
effectDisplay: "Keep all prestige upgrades on booster reset and buy max infinity points",
|
||||
done() { return getBuyableAmount("p",23).gte(5) },
|
||||
unlocked(){return (getBuyableAmount("p",23).gt(0)||hasMilestone(this.layer,this.id))}
|
||||
},
|
||||
13: {
|
||||
requirementDisplay: "10 boosters",
|
||||
effectDisplay: "Generators cost nothing",
|
||||
done() { return getBuyableAmount("p",23).gte(10) },
|
||||
unlocked(){return (getBuyableAmount("p",23).gt(0)||hasMilestone(this.layer,this.id))}
|
||||
},
|
||||
14: {
|
||||
requirementDisplay: "15 boosters",
|
||||
effectDisplay: "Auto buy the first 3 buyables and buy max pointy prestige points",
|
||||
done() { return getBuyableAmount("p",23).gte(15) },
|
||||
unlocked(){return (getBuyableAmount("p",41).gt(0)||hasMilestone(this.layer,this.id))}
|
||||
},
|
||||
15: {
|
||||
requirementDisplay: "20 boosters",
|
||||
effectDisplay: "Boosters reset nothing and auto booster",
|
||||
done() { return getBuyableAmount("p",23).gte(16) },
|
||||
unlocked(){return (getBuyableAmount("p",41).gt(0)||hasMilestone(this.layer,this.id))}
|
||||
},
|
||||
},
|
||||
passiveGeneration(){return (hasUpgrade("i",11)?1:0)},
|
||||
update(diff){
|
||||
if (hasMilestone(this.layer,2)&&!hasUpgrade(this.layer,54)){
|
||||
player[this.layer].upgrades.push(54)
|
||||
}
|
||||
if (hasMilestone(this.layer,1)&&!hasUpgrade(this.layer,11)&&!hasMilestone(this.layer,3)){
|
||||
player[this.layer].upgrades=[11,12,13,14,21,22,23,24]
|
||||
}
|
||||
if (hasMilestone(this.layer,3)&&!hasUpgrade(this.layer,31)){
|
||||
player[this.layer].upgrades=[11,12,13,14,21,22,23,24,31,32,33,34,41,42,43,44,51,52,53,54,61,62,63,64]
|
||||
}
|
||||
if (hasMilestone(this.layer,5)){
|
||||
player[this.layer].gp=player[this.layer].gp.plus(player.p.g.times(diff).times(player.p.geff))
|
||||
}
|
||||
let geff=new Decimal(1)
|
||||
if (hasUpgrade("p",81)) geff=geff.plus(2)
|
||||
if (hasUpgrade("p",102)) geff=geff.plus(hasChallenge("p",22)?player.p.gp.plus(1).log(10):player.p.gp.plus(1).log(10).plus(1).log(10))
|
||||
if (hasUpgrade("p",83)) geff=geff.times(player.p.points.plus(1).log(10).plus(1))
|
||||
if (hasUpgrade("p",94)) geff=geff.times(player.p.cmult)
|
||||
if (hasUpgrade("p",104)) geff=geff.times(new Decimal(player.p.buyables[21]).plus(1))
|
||||
if (hasUpgrade("p",143)) geff=geff.times(new Decimal(player.timePlayed).div(3600).max(1))
|
||||
if (hasUpgrade("p",225)) geff=geff.pow(new Decimal(player.p.buyables[23]).div(10).mul(new Decimal(0.1).plus(layers.p.buyables[41].effect).times(10)).plus(1))
|
||||
player.p.geff=geff
|
||||
if (hasChallenge("p",22)&&(!hasUpgrade("p",141)||hasUpgrade("i",12)))player.p.cmult=player.p.cmult.plus(hasUpgrade("p",141)?1:0.01)
|
||||
if (!hasUpgrade("p",111)) player.p.cmult=player.p.cmult.min(100)
|
||||
if (hasMilestone(this.layer,14)){
|
||||
if (layers.p.buyables[31].canAfford())layers.p.buyables[31].buy()
|
||||
if (layers.p.buyables[32].canAfford())layers.p.buyables[32].buy()
|
||||
if (layers.p.buyables[33].canAfford())layers.p.buyables[33].buy()
|
||||
}
|
||||
if (hasMilestone(this.layer,15)){
|
||||
if (layers.p.buyables[23].canAfford())layers.p.buyables[23].buy()
|
||||
}
|
||||
},
|
||||
subtabs: {
|
||||
"Upgrades": {
|
||||
display: `
|
||||
<main-display />
|
||||
<spacer />
|
||||
<prestige-button display="" />
|
||||
<spacer />
|
||||
<spacer />
|
||||
<upgrades />`
|
||||
},
|
||||
"Challenges": {
|
||||
unlocked() { return hasUpgrade("p", 51) || hasMilestone("p", 0); },
|
||||
display: `
|
||||
<spacer />
|
||||
<spacer />
|
||||
<challenges />`
|
||||
},
|
||||
"Buyables and Milestones": {
|
||||
unlocked(){return hasUpgrade("p",74)||hasMilestone("p",0)},
|
||||
display: `
|
||||
<spacer />
|
||||
<spacer />
|
||||
<row><buyable id="11" /></row>
|
||||
<spacer />
|
||||
<div v-if="hasMilestone('p', 0)">Your boosts are making the point challenge {{ getBuyableAmount('p', 11).plus(1) }}x less pointy</div>
|
||||
<spacer />
|
||||
<milestones />`
|
||||
},
|
||||
"Generators": {
|
||||
unlocked(){return hasMilestone("p",5)||player.i.points.gte(1)},
|
||||
display: `
|
||||
<spacer />
|
||||
<div>You have {{ format(player.p.gp) }} generator points, adding {{ format(hasUpgrade("p",132)?player.p.gp.plus(1).pow(new Decimal(1).div(2)):hasUpgrade("p",101)?player.p.gp.plus(1).pow(new Decimal(1).div(3)):hasUpgrade("p",93)?player.p.gp.plus(1).pow(0.2):player.p.gp.plus(1).log10()) }} to point gain</div>
|
||||
<div>You have {{ format(player.p.g) }} generators, generating {{ format(player.p.g.times(player.p.geff)) }} generator points per second</div>
|
||||
<div>Generator efficiency is {{ format(player.p.geff) }}</div>
|
||||
<spacer />
|
||||
<spacer />
|
||||
<buyables :buyables="[12, 13, 14]" />
|
||||
<row><clickable id="11" /></row>`
|
||||
},
|
||||
"Pointy Points": {
|
||||
unlocked(){return hasUpgrade("p",104)||player.i.points.gte(1)},
|
||||
display: `
|
||||
<div style="color: red; font-size: 32px; font-family: Comic Sans MS">{{ format(player.p.buyables[21]) }} pointy points</div>
|
||||
<div style="color: red; font-size: 32px; font-family: Comic Sans MS">My pointy points are multiplying generator efficiency by {{ format(new Decimal(player.p.buyables[21]).plus(1)) }}</div>
|
||||
<spacer />
|
||||
<spacer />
|
||||
<row><buyable id="21" /></row>
|
||||
<div v-if="hasMilestone('i', 5)" style="color: red; font-size: 32px; font-family: Comic Sans MS">I have {{ format(player.p.buyables[22]) }} pointy prestige points</div>
|
||||
<row><buyable id="22" /></row>
|
||||
<spacer />
|
||||
<upgrades :upgrades="[211, 212, 213, 214, 215]" />
|
||||
<upgrades :upgrades="[221, 222, 223, 224, 225]" />
|
||||
<div v-if="hasMilestone('p', 225)" style="color: red; font-size: 32px; font-family: Comic Sans MS">I have {{ format(player.p.buyables[23]) }} pointy boosters!</div>
|
||||
<row><buyable id="23" /></row>
|
||||
<div v-if="hasMilestone('p', 225) || getBuyableAmount('p', 23).gt(0)" style="color: red; font-size: 32px; font-family: Comic Sans MS">My pointy boosters are raising generator efficiency to the ^{{ format(new Decimal(player.p.buyables[23]).div(10).mul(new Decimal(0.1).plus(layers.p.buyables[41].effect).times(10)).plus(1)) }}</div>
|
||||
<spacer />
|
||||
<spacer />
|
||||
<div v-if="hasMilestone('p', 11)" style="font-size: 24px">Booster upgrades</div>
|
||||
<upgrades :upgrades="[231, 232, 233, 234, 235]" />`
|
||||
},
|
||||
"Buyables": {
|
||||
unlocked(){return hasMilestone("p",13)},
|
||||
display: `
|
||||
<buyables :buyables="[31, 32, 33]" />
|
||||
<buyables :buyables="[41, 42, 43]" />`
|
||||
}
|
||||
}
|
||||
}
|
2301
src/data/layers/demo.ts
Normal file
2301
src/data/layers/demo.ts
Normal file
File diff suppressed because it is too large
Load diff
120
src/data/mod.js
120
src/data/mod.js
|
@ -1,120 +0,0 @@
|
|||
import { computed } from 'vue';
|
||||
import { hasUpgrade, upgradeEffect, hasMilestone, inChallenge, getBuyableAmount } from '../util/features';
|
||||
import { layers } from '../game/layers';
|
||||
import player from '../game/player';
|
||||
import Decimal from '../util/bignum';
|
||||
|
||||
// Import initial layers
|
||||
import f from './layers/aca/f.js';
|
||||
import c from './layers/aca/c.js';
|
||||
import a from './layers/aca/a.js';
|
||||
import demoLayer from './layers/demo.js';
|
||||
import demoInfinityLayer from './layers/demo-infinity.js';
|
||||
const g = {
|
||||
id: "g",
|
||||
symbol: "TH",
|
||||
branches: ["c"],
|
||||
color: '#6d3678',
|
||||
shown: true,
|
||||
canClick() {return player.points.gte(10)},
|
||||
tooltip: "Thanos your points",
|
||||
click() {
|
||||
player.points = player.points.div(2);
|
||||
console.log(this.layer);
|
||||
}
|
||||
};
|
||||
const h = {
|
||||
id: "h",
|
||||
branches: ["g", () => ({ target: 'flatBoi', featureType: 'bar', endOffset: { x: -50 + 100 * layers.c.bars.flatBoi.progress.toNumber() } })],
|
||||
tooltip() {return "Restore your points to {{ player.c.otherThingy }}"},
|
||||
row: "side",
|
||||
position: 3,
|
||||
canClick() {return player.points.lt(player.c.otherThingy)},
|
||||
click() {player.points = new Decimal(player.c.otherThingy)}
|
||||
};
|
||||
const spook = {
|
||||
id: "spook",
|
||||
row: 1,
|
||||
layerShown: "ghost",
|
||||
};
|
||||
|
||||
const main = {
|
||||
id: 'main',
|
||||
display: `
|
||||
<div v-if="player.devSpeed === 0">Game Paused</div>
|
||||
<div v-else-if="player.devSpeed && player.devSpeed !== 1">Dev Speed: {{ format(player.devSpeed) }}x</div>
|
||||
<div v-if="player.offTime != undefined">Offline Time: {{ formatTime(player.offTime.remain) }}</div>
|
||||
<div>
|
||||
<span v-if="player.points.lt('1e1000')">You have </span>
|
||||
<h2>{{ format(player.points) }}</h2>
|
||||
<span v-if="player.points.lt('1e1e6')"> points</span>
|
||||
</div>
|
||||
<div v-if="Decimal.gt(pointGain, 0)">
|
||||
({{ player.oompsMag != 0 ? format(player.oomps) + " OOM" + (player.oompsMag < 0 ? "^OOM" : player.oompsMag > 1 ? "^" + player.oompsMag : "") + "s" : formatSmall(pointGain) }}/sec)
|
||||
</div>
|
||||
<spacer />
|
||||
<tree :append="true" />`,
|
||||
name: "Tree"
|
||||
};
|
||||
|
||||
export const getInitialLayers = () => [ main, f, c, a, g, h, spook, demoLayer, demoInfinityLayer ];
|
||||
|
||||
export function getStartingData() {
|
||||
return {
|
||||
points: new Decimal(10),
|
||||
}
|
||||
}
|
||||
|
||||
export const hasWon = computed(() => {
|
||||
return false;
|
||||
});
|
||||
|
||||
export const pointGain = computed(() => {
|
||||
if(!hasUpgrade("c", 11))
|
||||
return new Decimal(0);
|
||||
let gain = new Decimal(3.19)
|
||||
if (hasUpgrade("c", 12)) gain = gain.times(upgradeEffect("c", 12))
|
||||
if (hasMilestone("p",0))gain=gain.plus(0.01)
|
||||
if (hasMilestone("p",4)){
|
||||
if (hasUpgrade("p",12))gain=gain.plus(0.1)
|
||||
if (hasUpgrade("p",13))gain=gain.plus(0.1)
|
||||
if (hasUpgrade("p",14))gain=gain.plus(0.1)
|
||||
if (hasUpgrade("p",21))gain=gain.plus(0.1)
|
||||
if (hasUpgrade("p",22))gain=gain.plus(0.1)
|
||||
if (hasUpgrade("p",23))gain=gain.plus(0.1)
|
||||
if (hasUpgrade("p",31))gain=gain.plus(0.1)
|
||||
if (hasUpgrade("p",32))gain=gain.plus(0.1)
|
||||
if (hasUpgrade("p",33))gain=gain.plus(0.1)
|
||||
}
|
||||
if (hasUpgrade("p",11))gain=gain.plus(hasUpgrade("p",34)?(new Decimal(1).plus(layers.p.upgrades[34].effect)):1)
|
||||
if (hasUpgrade("p",12))gain=gain.times(hasUpgrade("p",34)?(new Decimal(1).plus(layers.p.upgrades[34].effect)):1)
|
||||
if (hasUpgrade("p",13))gain=gain.pow(hasUpgrade("p",34)?(new Decimal(1).plus(layers.p.upgrades[34].effect)):1)
|
||||
if (hasUpgrade("p",14))gain=gain.tetrate(hasUpgrade("p",34)?(new Decimal(1).plus(layers.p.upgrades[34].effect)):1)
|
||||
|
||||
if (hasUpgrade("p",71)) gain=gain.plus(1.1)
|
||||
if (hasUpgrade("p",72)) gain=gain.times(1.1)
|
||||
if (hasUpgrade("p",73)) gain=gain.pow(1.1)
|
||||
if (hasUpgrade("p",74)) gain=gain.tetrate(1.1)
|
||||
if (hasMilestone("p",5)&&!inChallenge("p",22)){
|
||||
let asdf = (hasUpgrade("p",132)?player.p.gp.plus(1).pow(new Decimal(1).div(2)):hasUpgrade("p",101)?player.p.gp.plus(1).pow(new Decimal(1).div(3)):hasUpgrade("p",93)?player.p.gp.plus(1).pow(0.2):player.p.gp.plus(1).log10())
|
||||
gain=gain.plus(asdf)
|
||||
if (hasUpgrade("p",213))gain=gain.mul(asdf.plus(1))
|
||||
}
|
||||
if (hasUpgrade("p",104)) gain=gain.times(player.p.points.plus(1).pow(0.5))
|
||||
if (hasUpgrade("p",142))gain=gain.times(5)
|
||||
if (player.i.unlocked)gain=gain.times(player.i.points.plus(1).pow(hasUpgrade("p",235)?6.9420:1))
|
||||
if (inChallenge("p",11)||inChallenge("p",21))gain=new Decimal(10).pow(gain.log10().pow(0.75))
|
||||
if (inChallenge("p",12)||inChallenge("p",21))gain=gain.pow(new Decimal(1).sub(new Decimal(1).div(getBuyableAmount("p",11).plus(1))))
|
||||
if (hasUpgrade("p",211))gain=gain.times(getBuyableAmount("p",21).plus(1))
|
||||
if (hasMilestone("p",13))gain=gain.times(layers.p.buyables[31].effect)
|
||||
if (hasMilestone("p",13))gain=gain.pow(layers.p.buyables[42].effect)
|
||||
return gain;
|
||||
});
|
||||
|
||||
/* eslint-disable-next-line no-unused-vars */
|
||||
export function update(delta) {
|
||||
}
|
||||
|
||||
/* eslint-disable-next-line no-unused-vars */
|
||||
export function fixOldSave(oldVersion, playerData) {
|
||||
}
|
187
src/data/mod.ts
Normal file
187
src/data/mod.ts
Normal file
|
@ -0,0 +1,187 @@
|
|||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { RawLayer } from "@/typings/layer";
|
||||
import { PlayerData } from "@/typings/player";
|
||||
import Decimal from "@/util/bignum";
|
||||
import {
|
||||
getBuyableAmount,
|
||||
hasMilestone,
|
||||
hasUpgrade,
|
||||
inChallenge,
|
||||
upgradeEffect
|
||||
} from "@/util/features";
|
||||
import { computed } from "vue";
|
||||
import a from "./layers/aca/a";
|
||||
import c from "./layers/aca/c";
|
||||
import f from "./layers/aca/f";
|
||||
import demoLayer from "./layers/demo";
|
||||
import demoInfinityLayer from "./layers/demo-infinity";
|
||||
|
||||
// Import initial layers
|
||||
|
||||
const g = {
|
||||
id: "g",
|
||||
symbol: "TH",
|
||||
branches: ["c"],
|
||||
color: "#6d3678",
|
||||
shown: true,
|
||||
canClick() {
|
||||
return player.points.gte(10);
|
||||
},
|
||||
tooltip: "Thanos your points",
|
||||
click() {
|
||||
player.points = player.points.div(2);
|
||||
console.log(this.layer);
|
||||
}
|
||||
} as RawLayer;
|
||||
const h = {
|
||||
id: "h",
|
||||
branches: [
|
||||
"g",
|
||||
() => ({
|
||||
target: "flatBoi",
|
||||
featureType: "bar",
|
||||
endOffset: {
|
||||
x:
|
||||
-50 +
|
||||
100 *
|
||||
(layers.c.bars!.data.flatBoi.progress instanceof Number
|
||||
? (layers.c.bars!.data.flatBoi.progress as number)
|
||||
: (layers.c.bars!.data.flatBoi.progress as Decimal).toNumber())
|
||||
}
|
||||
})
|
||||
],
|
||||
tooltip() {
|
||||
return "Restore your points to {{ player.c.otherThingy }}";
|
||||
},
|
||||
row: "side",
|
||||
position: 3,
|
||||
canClick() {
|
||||
return player.points.lt(player.layers.c.otherThingy as Decimal);
|
||||
},
|
||||
click() {
|
||||
player.points = new Decimal(player.layers.c.otherThingy as Decimal);
|
||||
}
|
||||
} as RawLayer;
|
||||
const spook = {
|
||||
id: "spook",
|
||||
row: 1,
|
||||
layerShown: "ghost"
|
||||
} as RawLayer;
|
||||
|
||||
const main = {
|
||||
id: "main",
|
||||
display: `
|
||||
<div v-if="player.devSpeed === 0">Game Paused</div>
|
||||
<div v-else-if="player.devSpeed && player.devSpeed !== 1">Dev Speed: {{ format(player.devSpeed) }}x</div>
|
||||
<div v-if="player.offTime != undefined">Offline Time: {{ formatTime(player.offTime.remain) }}</div>
|
||||
<div>
|
||||
<span v-if="player.points.lt('1e1000')">You have </span>
|
||||
<h2>{{ format(player.points) }}</h2>
|
||||
<span v-if="player.points.lt('1e1e6')"> points</span>
|
||||
</div>
|
||||
<div v-if="Decimal.gt(pointGain, 0)">
|
||||
({{ player.oompsMag != 0 ? format(player.oomps) + " OOM" + (player.oompsMag < 0 ? "^OOM" : player.oompsMag > 1 ? "^" + player.oompsMag : "") + "s" : formatSmall(pointGain) }}/sec)
|
||||
</div>
|
||||
<spacer />
|
||||
<tree :append="true" />`,
|
||||
name: "Tree"
|
||||
} as RawLayer;
|
||||
|
||||
export const getInitialLayers = (
|
||||
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
|
||||
playerData: Partial<PlayerData>
|
||||
): Array<RawLayer> => [main, f, c, a, g, h, spook, demoLayer, demoInfinityLayer];
|
||||
|
||||
export function getStartingData(): Record<string, unknown> {
|
||||
return {
|
||||
points: new Decimal(10)
|
||||
};
|
||||
}
|
||||
|
||||
export const hasWon = computed(() => {
|
||||
return false;
|
||||
});
|
||||
|
||||
export const pointGain = computed(() => {
|
||||
if (!hasUpgrade("c", 11)) return new Decimal(0);
|
||||
let gain = new Decimal(3.19);
|
||||
if (hasUpgrade("c", 12)) gain = gain.times(upgradeEffect("c", 12) as Decimal);
|
||||
if (hasMilestone("p", 0)) gain = gain.plus(0.01);
|
||||
if (hasMilestone("p", 4)) {
|
||||
if (hasUpgrade("p", 12)) gain = gain.plus(0.1);
|
||||
if (hasUpgrade("p", 13)) gain = gain.plus(0.1);
|
||||
if (hasUpgrade("p", 14)) gain = gain.plus(0.1);
|
||||
if (hasUpgrade("p", 21)) gain = gain.plus(0.1);
|
||||
if (hasUpgrade("p", 22)) gain = gain.plus(0.1);
|
||||
if (hasUpgrade("p", 23)) gain = gain.plus(0.1);
|
||||
if (hasUpgrade("p", 31)) gain = gain.plus(0.1);
|
||||
if (hasUpgrade("p", 32)) gain = gain.plus(0.1);
|
||||
if (hasUpgrade("p", 33)) gain = gain.plus(0.1);
|
||||
}
|
||||
if (hasUpgrade("p", 11))
|
||||
gain = gain.plus(
|
||||
hasUpgrade("p", 34)
|
||||
? new Decimal(1).plus(layers.p.upgrades!.data[34].effect as Decimal)
|
||||
: 1
|
||||
);
|
||||
if (hasUpgrade("p", 12))
|
||||
gain = gain.times(
|
||||
hasUpgrade("p", 34)
|
||||
? new Decimal(1).plus(layers.p.upgrades!.data[34].effect as Decimal)
|
||||
: 1
|
||||
);
|
||||
if (hasUpgrade("p", 13))
|
||||
gain = gain.pow(
|
||||
hasUpgrade("p", 34)
|
||||
? new Decimal(1).plus(layers.p.upgrades!.data[34].effect as Decimal)
|
||||
: 1
|
||||
);
|
||||
if (hasUpgrade("p", 14))
|
||||
gain = gain.tetrate(
|
||||
hasUpgrade("p", 34)
|
||||
? new Decimal(1).plus(layers.p.upgrades!.data[34].effect as Decimal).toNumber()
|
||||
: 1
|
||||
);
|
||||
|
||||
if (hasUpgrade("p", 71)) gain = gain.plus(1.1);
|
||||
if (hasUpgrade("p", 72)) gain = gain.times(1.1);
|
||||
if (hasUpgrade("p", 73)) gain = gain.pow(1.1);
|
||||
if (hasUpgrade("p", 74)) gain = gain.tetrate(1.1);
|
||||
if (hasMilestone("p", 5) && !inChallenge("p", 22)) {
|
||||
const asdf = hasUpgrade("p", 132)
|
||||
? (player.layers.p.gp as Decimal).plus(1).pow(new Decimal(1).div(2))
|
||||
: hasUpgrade("p", 101)
|
||||
? (player.layers.p.gp as Decimal).plus(1).pow(new Decimal(1).div(3))
|
||||
: hasUpgrade("p", 93)
|
||||
? (player.layers.p.gp as Decimal).plus(1).pow(0.2)
|
||||
: (player.layers.p.gp as Decimal).plus(1).log10();
|
||||
gain = gain.plus(asdf);
|
||||
if (hasUpgrade("p", 213)) gain = gain.mul(asdf.plus(1));
|
||||
}
|
||||
if (hasUpgrade("p", 104)) gain = gain.times(player.layers.p.points.plus(1).pow(0.5));
|
||||
if (hasUpgrade("p", 142)) gain = gain.times(5);
|
||||
if (player.layers.i.unlocked)
|
||||
gain = gain.times(player.layers.i.points.plus(1).pow(hasUpgrade("p", 235) ? 6.942 : 1));
|
||||
if (inChallenge("p", 11) || inChallenge("p", 21))
|
||||
gain = new Decimal(10).pow(gain.log10().pow(0.75));
|
||||
if (inChallenge("p", 12) || inChallenge("p", 21))
|
||||
gain = gain.pow(new Decimal(1).sub(new Decimal(1).div(getBuyableAmount("p", 11)!.plus(1))));
|
||||
if (hasUpgrade("p", 211)) gain = gain.times(getBuyableAmount("p", 21)!.plus(1));
|
||||
if (hasMilestone("p", 13)) gain = gain.times(layers.p.buyables!.data[31].effect as Decimal);
|
||||
if (hasMilestone("p", 13)) gain = gain.pow(layers.p.buyables!.data[42].effect as Decimal);
|
||||
return gain;
|
||||
});
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
export function update(delta: Decimal): void {}
|
||||
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
export function fixOldSave(
|
||||
oldVersion: string | undefined,
|
||||
playerData: Partial<PlayerData>
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
): void {}
|
||||
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
@ -1,52 +0,0 @@
|
|||
const defaultTheme = {
|
||||
variables: {
|
||||
"--background": "#0f0f0f",
|
||||
"--background-tooltip": "rgba(0, 0, 0, 0.75)",
|
||||
"--secondary-background": "#0f0f0f",
|
||||
"--color": "#dfdfdf",
|
||||
"--points": "#ffffff",
|
||||
"--locked": "#bf8f8f",
|
||||
"--bought": "#77bf5f",
|
||||
"--link": "#02f2f2",
|
||||
"--separator": "#dfdfdf",
|
||||
"--border-radius": "25%",
|
||||
"--danger": "rgb(220, 53, 69)",
|
||||
"--modal-border": "solid 2px var(--color)",
|
||||
"--feature-margin": "0px",
|
||||
},
|
||||
stackedInfoboxes: false,
|
||||
floatingTabs: true
|
||||
};
|
||||
|
||||
export default {
|
||||
classic: defaultTheme,
|
||||
paper: {
|
||||
...defaultTheme,
|
||||
variables: {
|
||||
...defaultTheme.variables,
|
||||
"--background": "#2a323d",
|
||||
"--secondary-background": "#333c4a",
|
||||
"--locked": "#3a3e45",
|
||||
"--bought": "#5C8A58",
|
||||
"--separator": "#333c4a",
|
||||
"--border-radius": "4px",
|
||||
"--modal-border": "",
|
||||
"--feature-margin": "5px",
|
||||
},
|
||||
stackedInfoboxes: true,
|
||||
floatingTabs: false
|
||||
},
|
||||
aqua: {
|
||||
...defaultTheme,
|
||||
variables: {
|
||||
...defaultTheme.variables,
|
||||
"--background": "#001f3f",
|
||||
"--background-tooltip": "rgba(0, 15, 31, 0.75)",
|
||||
"--secondary-background": "#001f3f",
|
||||
"--color": "#bfdfff",
|
||||
"--points": "#dfefff",
|
||||
"--locked": "#c4a7b3",
|
||||
"--separator": "#bfdfff"
|
||||
}
|
||||
}
|
||||
};
|
60
src/data/themes.ts
Normal file
60
src/data/themes.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { Theme } from "@/typings/theme";
|
||||
|
||||
const defaultTheme: Theme = {
|
||||
variables: {
|
||||
"--background": "#0f0f0f",
|
||||
"--background-tooltip": "rgba(0, 0, 0, 0.75)",
|
||||
"--secondary-background": "#0f0f0f",
|
||||
"--color": "#dfdfdf",
|
||||
"--points": "#ffffff",
|
||||
"--locked": "#bf8f8f",
|
||||
"--bought": "#77bf5f",
|
||||
"--link": "#02f2f2",
|
||||
"--separator": "#dfdfdf",
|
||||
"--border-radius": "25%",
|
||||
"--danger": "rgb(220, 53, 69)",
|
||||
"--modal-border": "solid 2px var(--color)",
|
||||
"--feature-margin": "0px"
|
||||
},
|
||||
stackedInfoboxes: false,
|
||||
floatingTabs: true
|
||||
};
|
||||
|
||||
export enum Themes {
|
||||
Classic = "classic",
|
||||
Paper = "paper",
|
||||
Aqua = "aqua"
|
||||
}
|
||||
|
||||
export default {
|
||||
classic: defaultTheme,
|
||||
paper: {
|
||||
...defaultTheme,
|
||||
variables: {
|
||||
...defaultTheme.variables,
|
||||
"--background": "#2a323d",
|
||||
"--secondary-background": "#333c4a",
|
||||
"--locked": "#3a3e45",
|
||||
"--bought": "#5C8A58",
|
||||
"--separator": "#333c4a",
|
||||
"--border-radius": "4px",
|
||||
"--modal-border": "",
|
||||
"--feature-margin": "5px"
|
||||
},
|
||||
stackedInfoboxes: true,
|
||||
floatingTabs: false
|
||||
} as Theme,
|
||||
aqua: {
|
||||
...defaultTheme,
|
||||
variables: {
|
||||
...defaultTheme.variables,
|
||||
"--background": "#001f3f",
|
||||
"--background-tooltip": "rgba(0, 15, 31, 0.75)",
|
||||
"--secondary-background": "#001f3f",
|
||||
"--color": "#bfdfff",
|
||||
"--points": "#dfefff",
|
||||
"--locked": "#c4a7b3",
|
||||
"--separator": "#bfdfff"
|
||||
}
|
||||
} as Theme
|
||||
} as Record<Themes, Theme>;
|
30
src/game/enums.ts
Normal file
30
src/game/enums.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
export enum LayerType {
|
||||
Static = "static",
|
||||
Normal = "normal",
|
||||
Custom = "custom",
|
||||
None = "none"
|
||||
}
|
||||
|
||||
export enum Direction {
|
||||
Up = "Up",
|
||||
Down = "Down",
|
||||
Left = "Left",
|
||||
Right = "Right",
|
||||
Default = "Up"
|
||||
}
|
||||
|
||||
export enum MilestoneDisplay {
|
||||
All = "all",
|
||||
Last = "last",
|
||||
Configurable = "configurable",
|
||||
Incomplete = "incomplete",
|
||||
None = "none"
|
||||
}
|
||||
|
||||
export enum ImportingStatus {
|
||||
NotImporting = "NOT_IMPORTING",
|
||||
Importing = "IMPORTING",
|
||||
Failed = "FAILED",
|
||||
WrongID = "WRONG_ID",
|
||||
Force = "FORCE"
|
||||
}
|
|
@ -1,156 +0,0 @@
|
|||
import { update as modUpdate, hasWon, pointGain } from '../data/mod';
|
||||
import Decimal from '../util/bignum';
|
||||
import modInfo from '../data/modInfo.json';
|
||||
import { layers } from './layers';
|
||||
import player from './player';
|
||||
|
||||
function updatePopups(/* diff */) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
function updateParticles(/* diff */) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
function updateOOMPS(diff) {
|
||||
if (player.points != undefined) {
|
||||
player.oompsMag = 0;
|
||||
if (player.points.lte(new Decimal(1e100))) {
|
||||
player.lastPoints = player.points;
|
||||
return;
|
||||
}
|
||||
|
||||
let curr = player.points;
|
||||
let prev = player.lastPoints || new Decimal(0);
|
||||
player.lastPoints = curr;
|
||||
if (curr.gt(prev)) {
|
||||
if (curr.gte("10^^8")) {
|
||||
curr = curr.slog(1e10);
|
||||
prev = prev.slog(1e10);
|
||||
player.oomps = curr.sub(prev).div(diff);
|
||||
player.oompsMag = -1;
|
||||
} else {
|
||||
while (curr.div(prev).log(10).div(diff).gte("100") && player.oompsMag <= 5 && prev.gt(0)) {
|
||||
curr = curr.log(10);
|
||||
prev = prev.log(10);
|
||||
player.oomps = curr.sub(prev).div(diff);
|
||||
player.oompsMag++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateLayers(diff) {
|
||||
// Update each active layer
|
||||
const activeLayers = Object.keys(layers).filter(layer => !layers[layer].deactivated);
|
||||
activeLayers.forEach(layer => {
|
||||
if (player[layer].resetTime != undefined) {
|
||||
player[layer].resetTime = player[layer].resetTime.add(diff);
|
||||
}
|
||||
if (layers[layer].passiveGeneration) {
|
||||
player[layer].points =
|
||||
player[layer].points.add(Decimal.times(layers[layer].resetGain, layers[layer].passiveGeneration).times(diff));
|
||||
}
|
||||
layers[layer].update?.(diff);
|
||||
});
|
||||
// Automate each active layer
|
||||
activeLayers.forEach(layer => {
|
||||
if (layers[layer].autoReset && layers[layer].canReset) {
|
||||
layers[layer].reset();
|
||||
}
|
||||
layers[layer].automate?.();
|
||||
if (layers[layer].upgrades && layers[layer].autoUpgrade) {
|
||||
Object.values(layers[layer].upgrades).forEach(upgrade => upgrade.buy());
|
||||
}
|
||||
});
|
||||
// Check each active layer for newly unlocked achievements or milestones
|
||||
activeLayers.forEach(layer => {
|
||||
if (layers[layer].milestones) {
|
||||
Object.values(layers[layer].milestones).forEach(milestone => {
|
||||
if (milestone.unlocked !== false && !milestone.earned && milestone.done) {
|
||||
player[layer].milestones.push(milestone.id);
|
||||
milestone.onComplete?.();
|
||||
// TODO popup notification
|
||||
player[layer].lastMilestone = milestone.id;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (layers[layer].achievements) {
|
||||
Object.values(layers[layer].achievements).forEach(achievement => {
|
||||
if (achievement.unlocked !== false && !achievement.earned && achievement.done) {
|
||||
player[layer].achievements.push(achievement.id);
|
||||
achievement.onComplete?.();
|
||||
// TODO popup notification
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function update() {
|
||||
let now = Date.now();
|
||||
let diff = (now - player.time) / 1e3;
|
||||
player.time = now;
|
||||
let trueDiff = diff;
|
||||
|
||||
// Always update UI
|
||||
updatePopups(trueDiff);
|
||||
updateParticles(trueDiff);
|
||||
player.lastTenTicks.push(trueDiff);
|
||||
if (player.lastTenTicks.length > 10) {
|
||||
player.lastTenTicks = player.lastTenTicks.slice(1);
|
||||
}
|
||||
|
||||
// Stop here if the game is paused on the win screen
|
||||
if (hasWon.value && !player.keepGoing) {
|
||||
return;
|
||||
}
|
||||
// Stop here if the player had a NaN value
|
||||
if (player.hasNaN) {
|
||||
return;
|
||||
}
|
||||
|
||||
diff = new Decimal(diff).max(0);
|
||||
|
||||
// Add offline time if any
|
||||
if (player.offTime != undefined) {
|
||||
if (player.offTime.remain > modInfo.offlineLimit * 3600) {
|
||||
player.offTime.remain = modInfo.offlineLimit * 3600;
|
||||
}
|
||||
if (player.offTime.remain > 0 && player.devSpeed !== 0) {
|
||||
let offlineDiff = Math.max(player.offTime.remain / 10, diff);
|
||||
player.offTime.remain -= offlineDiff;
|
||||
diff = diff.add(offlineDiff);
|
||||
} else if (player.devSpeed === 0) {
|
||||
player.offTime.remain += diff.toNumber();
|
||||
}
|
||||
if (!player.offlineProd || player.offTime.remain <= 0) {
|
||||
player.offTime = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Cap at max tick length
|
||||
diff = Decimal.min(diff, modInfo.maxTickLength);
|
||||
|
||||
// Apply dev speed
|
||||
if (player.devSpeed != undefined) {
|
||||
diff = diff.times(player.devSpeed);
|
||||
}
|
||||
|
||||
// Update
|
||||
if (diff.eq(0)) {
|
||||
return;
|
||||
}
|
||||
player.timePlayed = player.timePlayed.add(diff);
|
||||
if (player.points != undefined) {
|
||||
player.points = player.points.add(Decimal.times(pointGain.value, diff));
|
||||
}
|
||||
modUpdate(diff);
|
||||
updateOOMPS(trueDiff);
|
||||
updateLayers(diff);
|
||||
}
|
||||
|
||||
export default function startGameLoop() {
|
||||
setInterval(update, 50);
|
||||
}
|
171
src/game/gameLoop.ts
Normal file
171
src/game/gameLoop.ts
Normal file
|
@ -0,0 +1,171 @@
|
|||
import { hasWon, pointGain, update as modUpdate } from "@/data/mod";
|
||||
import modInfo from "@/data/modInfo.json";
|
||||
import Decimal, { DecimalSource } from "@/util/bignum";
|
||||
import { layers } from "./layers";
|
||||
import player from "./player";
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
|
||||
function updatePopups(diff: number) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
|
||||
function updateParticles(diff: number) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
function updateOOMPS(diff: DecimalSource) {
|
||||
if (player.points != undefined) {
|
||||
player.oompsMag = 0;
|
||||
if (player.points.lte(new Decimal(1e100))) {
|
||||
player.lastPoints = player.points;
|
||||
return;
|
||||
}
|
||||
|
||||
let curr = player.points;
|
||||
let prev = (player.lastPoints as Decimal) || new Decimal(0);
|
||||
player.lastPoints = curr;
|
||||
if (curr.gt(prev)) {
|
||||
if (curr.gte("10^^8")) {
|
||||
curr = curr.slog(1e10);
|
||||
prev = prev.slog(1e10);
|
||||
player.oomps = curr.sub(prev).div(diff);
|
||||
player.oompsMag = -1;
|
||||
} else {
|
||||
while (
|
||||
curr
|
||||
.div(prev)
|
||||
.log(10)
|
||||
.div(diff)
|
||||
.gte("100") &&
|
||||
player.oompsMag <= 5 &&
|
||||
prev.gt(0)
|
||||
) {
|
||||
curr = curr.log(10);
|
||||
prev = prev.log(10);
|
||||
player.oomps = curr.sub(prev).div(diff);
|
||||
player.oompsMag++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateLayers(diff: DecimalSource) {
|
||||
// Update each active layer
|
||||
const activeLayers = Object.keys(layers).filter(layer => !layers[layer].deactivated);
|
||||
activeLayers.forEach(layer => {
|
||||
if (player.layers[layer].resetTime != undefined) {
|
||||
player.layers[layer].resetTime = player.layers[layer].resetTime.add(diff);
|
||||
}
|
||||
if (layers[layer].passiveGeneration) {
|
||||
const passiveGeneration =
|
||||
typeof layers[layer].passiveGeneration == "boolean"
|
||||
? 1
|
||||
: (layers[layer].passiveGeneration as DecimalSource);
|
||||
player.layers[layer].points = player.layers[layer].points.add(
|
||||
Decimal.times(layers[layer].resetGain, passiveGeneration).times(diff)
|
||||
);
|
||||
}
|
||||
layers[layer].update?.(diff);
|
||||
});
|
||||
// Automate each active layer
|
||||
activeLayers.forEach(layer => {
|
||||
if (layers[layer].autoReset && layers[layer].canReset) {
|
||||
layers[layer].reset();
|
||||
}
|
||||
layers[layer].automate?.();
|
||||
if (layers[layer].upgrades && layers[layer].autoUpgrade) {
|
||||
Object.values(layers[layer].upgrades!.data).forEach(upgrade => upgrade.buy());
|
||||
}
|
||||
});
|
||||
// Check each active layer for newly unlocked achievements or milestones
|
||||
activeLayers.forEach(layer => {
|
||||
if (layers[layer].milestones) {
|
||||
Object.values(layers[layer].milestones!.data).forEach(milestone => {
|
||||
if (milestone.unlocked !== false && !milestone.earned && milestone.done) {
|
||||
player.layers[layer].milestones.push(milestone.id);
|
||||
milestone.onComplete?.();
|
||||
// TODO popup notification
|
||||
player.layers[layer].lastMilestone = milestone.id;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (layers[layer].achievements) {
|
||||
Object.values(layers[layer].achievements!.data).forEach(achievement => {
|
||||
if (achievement.unlocked !== false && !achievement.earned && achievement.done) {
|
||||
player.layers[layer].achievements.push(achievement.id);
|
||||
achievement.onComplete?.();
|
||||
// TODO popup notification
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function update() {
|
||||
const now = Date.now();
|
||||
let diff: DecimalSource = (now - player.time) / 1e3;
|
||||
player.time = now;
|
||||
const trueDiff = diff;
|
||||
|
||||
// Always update UI
|
||||
updatePopups(trueDiff);
|
||||
updateParticles(trueDiff);
|
||||
player.lastTenTicks.push(trueDiff);
|
||||
if (player.lastTenTicks.length > 10) {
|
||||
player.lastTenTicks = player.lastTenTicks.slice(1);
|
||||
}
|
||||
|
||||
// Stop here if the game is paused on the win screen
|
||||
if (hasWon.value && !player.keepGoing) {
|
||||
return;
|
||||
}
|
||||
// Stop here if the player had a NaN value
|
||||
if (player.hasNaN) {
|
||||
return;
|
||||
}
|
||||
|
||||
diff = new Decimal(diff).max(0);
|
||||
|
||||
// Add offline time if any
|
||||
if (player.offlineTime != undefined) {
|
||||
if (player.offlineTime.gt(modInfo.offlineLimit * 3600)) {
|
||||
player.offlineTime = new Decimal(modInfo.offlineLimit * 3600);
|
||||
}
|
||||
if (player.offlineTime.gt(0) && player.devSpeed !== 0) {
|
||||
const offlineDiff = Decimal.max(player.offlineTime.div(10), diff);
|
||||
player.offlineTime = player.offlineTime.sub(offlineDiff);
|
||||
diff = diff.add(offlineDiff);
|
||||
} else if (player.devSpeed === 0) {
|
||||
player.offlineTime = player.offlineTime.add(diff);
|
||||
}
|
||||
if (!player.offlineProd || player.offlineTime.lt(0)) {
|
||||
player.offlineTime = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Cap at max tick length
|
||||
diff = Decimal.min(diff, modInfo.maxTickLength);
|
||||
|
||||
// Apply dev speed
|
||||
if (player.devSpeed != undefined) {
|
||||
diff = diff.times(player.devSpeed);
|
||||
}
|
||||
|
||||
// Update
|
||||
if (diff.eq(0)) {
|
||||
return;
|
||||
}
|
||||
player.timePlayed = player.timePlayed.add(diff);
|
||||
if (player.points != undefined) {
|
||||
player.points = player.points.add(Decimal.times(pointGain.value, diff));
|
||||
}
|
||||
modUpdate(diff);
|
||||
updateOOMPS(trueDiff);
|
||||
updateLayers(diff);
|
||||
}
|
||||
|
||||
export default function startGameLoop(): void {
|
||||
setInterval(update, 50);
|
||||
}
|
|
@ -1,452 +0,0 @@
|
|||
import clone from 'lodash.clonedeep';
|
||||
import { isFunction, isPlainObject } from '../util/common';
|
||||
import { createProxy, createGridProxy } from '../util/proxies';
|
||||
import playerProxy from './player';
|
||||
import Decimal from '../util/bignum';
|
||||
import { noCache, getStartingBuyables, getStartingClickables, getStartingChallenges, defaultLayerProperties } from '../util/layers';
|
||||
import { applyPlayerData } from '../util/save';
|
||||
import { isRef } from 'vue';
|
||||
|
||||
export const layers = {};
|
||||
export const hotkeys = [];
|
||||
window.layers = layers;
|
||||
|
||||
export function addLayer(layer, player = null) {
|
||||
player = player || playerProxy;
|
||||
|
||||
// Check for required properties
|
||||
if (!('id' in layer)) {
|
||||
console.error(`Cannot add layer without a "id" property!`, layer);
|
||||
return;
|
||||
}
|
||||
if (layer.type === "static" || layer.type === "normal") {
|
||||
const missingProperty = [ 'baseAmount', 'requires' ].find(prop => !(prop in layer));
|
||||
if (missingProperty) {
|
||||
console.error(`Cannot add layer without a "${missingProperty}" property!`, layer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Clone object to prevent modifying the original
|
||||
layer = clone(layer);
|
||||
|
||||
player[layer.id] = applyPlayerData({
|
||||
upgrades: [],
|
||||
achievements: [],
|
||||
milestones: [],
|
||||
infoboxes: {},
|
||||
buyables: getStartingBuyables(layer),
|
||||
clickables: getStartingClickables(layer),
|
||||
challenges: getStartingChallenges(layer),
|
||||
grids: {},
|
||||
...layer.startData?.()
|
||||
}, player[layer.id]);
|
||||
|
||||
// Set default property values
|
||||
layer = Object.assign({}, defaultLayerProperties, layer);
|
||||
layer.layer = layer.id;
|
||||
if (layer.type === "static" && (layer.base == undefined || Decimal.lte(layer.base, 1))) {
|
||||
layer.base = 2;
|
||||
}
|
||||
|
||||
// Process each feature
|
||||
for (let property of uncachedProperties) {
|
||||
if (layer[property] && !isRef(layer.property)) {
|
||||
layer[property].forceCached = false;
|
||||
}
|
||||
}
|
||||
for (let property of gridProperties) {
|
||||
if (layer[property]) {
|
||||
setRowCol(layer[property]);
|
||||
}
|
||||
}
|
||||
for (let property of featureProperties) {
|
||||
if (layer[property]) {
|
||||
setupFeature(layer.id, layer[property]);
|
||||
}
|
||||
}
|
||||
if (layer.upgrades) {
|
||||
for (let id in layer.upgrades) {
|
||||
if (isPlainObject(layer.upgrades[id])) {
|
||||
layer.upgrades[id].bought = function() {
|
||||
return !layer.deactivated && playerProxy[layer.id].upgrades.some(upgrade => upgrade == id);
|
||||
}
|
||||
setDefault(layer.upgrades[id], 'canAfford', function() {
|
||||
if (this.currencyInternalName) {
|
||||
let name = this.currencyInternalName;
|
||||
if (this.currencyLocation) {
|
||||
return !(this.currencyLocation[name].lt(this.cost));
|
||||
} else if (this.currencyLayer) {
|
||||
let lr = this.currencyLayer;
|
||||
return !(playerProxy[lr][name].lt(this.cost));
|
||||
} else {
|
||||
return !(playerProxy[name].lt(this.cost));
|
||||
}
|
||||
} else {
|
||||
return !(playerProxy[this.layer].points.lt(this.cost))
|
||||
}
|
||||
});
|
||||
setDefault(layer.upgrades[id], 'pay', function() {
|
||||
if (this.bought || !this.canAfford) {
|
||||
return;
|
||||
}
|
||||
if (this.currencyInternalName) {
|
||||
let name = this.currencyInternalName
|
||||
if (this.currencyLocation) {
|
||||
if (this.currencyLocation[name].lt(this.cost)) {
|
||||
return;
|
||||
}
|
||||
this.currencyLocation[name] = this.currencyLocation[name].sub(this.cost);
|
||||
} else if (this.currencyLayer) {
|
||||
let lr = this.currencyLayer;
|
||||
if (playerProxy[lr][name].lt(this.cost)) {
|
||||
return;
|
||||
}
|
||||
playerProxy[lr][name] = playerProxy[lr][name].sub(this.cost);
|
||||
} else {
|
||||
if (playerProxy[name].lt(this.cost)) {
|
||||
return;
|
||||
}
|
||||
playerProxy[name] = playerProxy[name].sub(this.cost);
|
||||
}
|
||||
} else {
|
||||
if (playerProxy[this.layer].points.lt(this.cost)) {
|
||||
return;
|
||||
}
|
||||
playerProxy[this.layer].points = playerProxy[this.layer].points.sub(this.cost);
|
||||
}
|
||||
}, false);
|
||||
setDefault(layer.upgrades[id], 'buy', function() {
|
||||
if (this.bought || !this.canAfford) {
|
||||
return;
|
||||
}
|
||||
this.pay();
|
||||
playerProxy[this.layer].upgrades.push(this.id);
|
||||
this.onPurchase?.();
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layer.achievements) {
|
||||
for (let id in layer.achievements) {
|
||||
if (isPlainObject(layer.achievements[id])) {
|
||||
layer.achievements[id].earned = function() {
|
||||
return !layer.deactivated && playerProxy[layer.id].achievements.some(achievement => achievement == id);
|
||||
}
|
||||
setDefault(layer.achievements[id], 'onComplete', null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layer.challenges) {
|
||||
layer.activeChallenge = function() {
|
||||
return Object.values(this.challenges).find(challenge => challenge.active);
|
||||
}
|
||||
for (let id in layer.challenges) {
|
||||
if (isPlainObject(layer.challenges[id])) {
|
||||
layer.challenges[id].shown = function() {
|
||||
return this.unlocked !== false && (playerProxy.hideChallenges === false || !this.maxed);
|
||||
}
|
||||
layer.challenges[id].completed = function() {
|
||||
return !layer.deactivated && playerProxy[layer.id].challenges[id]?.gt(0);
|
||||
}
|
||||
layer.challenges[id].completions = function() {
|
||||
return playerProxy[layer.id].challenges[id];
|
||||
}
|
||||
layer.challenges[id].maxed = function() {
|
||||
return !layer.deactivated && Decimal.gte(playerProxy[layer.id].challenges[id], this.completionLimit);
|
||||
}
|
||||
layer.challenges[id].active = function() {
|
||||
return !layer.deactivated && playerProxy[layer.id].activeChallenge === id;
|
||||
}
|
||||
layer.challenges[id].toggle = noCache(function() {
|
||||
let exiting = playerProxy[layer.id].activeChallenge === id;
|
||||
if (exiting) {
|
||||
if (this.canComplete && !this.maxed) {
|
||||
let completions = this.canComplete;
|
||||
if (completions === true) {
|
||||
completions = 1;
|
||||
}
|
||||
playerProxy[layer.id].challenges[id] =
|
||||
Decimal.min(playerProxy[layer.id].challenges[id].add(completions), this.completionLimit);
|
||||
this.onComplete?.();
|
||||
}
|
||||
playerProxy[layer.id].activeChallenge = null;
|
||||
this.onExit?.();
|
||||
layer.reset(true);
|
||||
} else if (!exiting && this.canStart) {
|
||||
layer.reset(true);
|
||||
playerProxy[layer.id].activeChallenge = id;
|
||||
this.onEnter?.();
|
||||
}
|
||||
});
|
||||
setDefault(layer.challenges[id], 'onComplete', null, false);
|
||||
setDefault(layer.challenges[id], 'onEnter', null, false);
|
||||
setDefault(layer.challenges[id], 'onExit', null, false);
|
||||
setDefault(layer.challenges[id], 'canStart', true);
|
||||
setDefault(layer.challenges[id], 'completionLimit', new Decimal(1));
|
||||
setDefault(layer.challenges[id], 'mark', function() {
|
||||
return Decimal.gt(this.completionLimit, 1) && this.maxed;
|
||||
});
|
||||
setDefault(layer.challenges[id], 'canComplete', function() {
|
||||
if (!this.active) {
|
||||
return false;
|
||||
}
|
||||
if (this.currencyInternalName) {
|
||||
let name = this.currencyInternalName;
|
||||
if (this.currencyLocation) {
|
||||
return !(this.currencyLocation[name].lt(this.goal));
|
||||
} else if (this.currencyLayer) {
|
||||
let lr = this.currencyLayer;
|
||||
return !(playerProxy[lr][name].lt(this.goal));
|
||||
} else {
|
||||
return !(playerProxy[name].lt(this.goal));
|
||||
}
|
||||
} else {
|
||||
return !(playerProxy.points.lt(this.goal));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layer.buyables) {
|
||||
setDefault(layer.buyables, 'respec', null, false);
|
||||
setDefault(layer.buyables, 'reset', function() {
|
||||
playerProxy[this.layer].buyables = getStartingBuyables(layer);
|
||||
}, false);
|
||||
for (let id in layer.buyables) {
|
||||
if (isPlainObject(layer.buyables[id])) {
|
||||
layer.buyables[id].amount = function() {
|
||||
return playerProxy[layer.id].buyables[id];
|
||||
}
|
||||
layer.buyables[id].amountSet = function(amount) {
|
||||
playerProxy[layer.id].buyables[id] = amount;
|
||||
}
|
||||
layer.buyables[id].canBuy = function() {
|
||||
return !layer.deactivated && this.unlocked !== false && this.canAfford !== false &&
|
||||
Decimal.lt(playerProxy[layer.id].buyables[id], this.purchaseLimit);
|
||||
}
|
||||
setDefault(layer.buyables[id], 'purchaseLimit', new Decimal(Infinity));
|
||||
setDefault(layer.buyables[id], 'sellOne', null, false);
|
||||
setDefault(layer.buyables[id], 'sellAll', null, false);
|
||||
if (layer.buyables[id].cost != undefined) {
|
||||
setDefault(layer.buyables[id], 'buy', function() {
|
||||
if (this.canBuy) {
|
||||
playerProxy[this.layer].points = playerProxy[this.layer].points.sub(this.cost());
|
||||
this.amount = this.amount.add(1);
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layer.clickables) {
|
||||
layer.clickables.layer = layer.id;
|
||||
setDefault(layer.clickables, 'masterButtonClick', null, false);
|
||||
if (layer.clickables.masterButtonDisplay != undefined) {
|
||||
setDefault(layer.clickables, 'showMaster', true);
|
||||
}
|
||||
for (let id in layer.clickables) {
|
||||
if (isPlainObject(layer.clickables[id])) {
|
||||
layer.clickables[id].state = function() {
|
||||
return playerProxy[layer.id].clickables[id];
|
||||
}
|
||||
layer.clickables[id].stateSet = function(state) {
|
||||
playerProxy[layer.id].clickables[id] = state;
|
||||
}
|
||||
setDefault(layer.clickables[id], 'click', null, false);
|
||||
setDefault(layer.clickables[id], 'hold', null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layer.milestones) {
|
||||
for (let id in layer.milestones) {
|
||||
if (isPlainObject(layer.milestones[id])) {
|
||||
layer.milestones[id].earned = function() {
|
||||
return !layer.deactivated && playerProxy[layer.id].milestones.some(milestone => milestone == id);
|
||||
}
|
||||
layer.milestones[id].shown = function() {
|
||||
if (!this.unlocked) {
|
||||
return false;
|
||||
}
|
||||
switch (playerProxy.msDisplay) {
|
||||
default:
|
||||
case "all":
|
||||
return true;
|
||||
case "last":
|
||||
return this.optionsDisplay || !this.earned ||
|
||||
playerProxy[this.layer].milestones[playerProxy[this.layer].milestones.length - 1] === this.id;
|
||||
case "configurable":
|
||||
return this.optionsDisplay || !this.earned;
|
||||
case "incomplete":
|
||||
return !this.earned;
|
||||
case "none":
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layer.grids) {
|
||||
for (let id in layer.grids) {
|
||||
if (isPlainObject(layer.grids[id])) {
|
||||
setDefault(player[layer.id].grids, id, {});
|
||||
layer.grids[id].getData = function(cell) {
|
||||
if (playerProxy[layer.id].grids[id][cell] != undefined) {
|
||||
return playerProxy[layer.id].grids[id][cell];
|
||||
}
|
||||
if (isFunction(this.getStartData)) {
|
||||
return this.getStartData(cell);
|
||||
}
|
||||
return this.getStartData;
|
||||
}
|
||||
layer.grids[id].dataSet = function(cell, data) {
|
||||
playerProxy[layer.id].grids[id][cell] = data;
|
||||
}
|
||||
setDefault(layer.grids[id], 'getUnlocked', true, false);
|
||||
setDefault(layer.grids[id], 'getCanClick', true, false);
|
||||
setDefault(layer.grids[id], 'getStartData', "", false);
|
||||
setDefault(layer.grids[id], 'getStyle', null, false);
|
||||
setDefault(layer.grids[id], 'click', null, false);
|
||||
setDefault(layer.grids[id], 'hold', null, false);
|
||||
setDefault(layer.grids[id], 'getTitle', null, false);
|
||||
setDefault(layer.grids[id], 'getDisplay', null, false);
|
||||
layer.grids[id] = createGridProxy(layer.grids[id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layer.subtabs) {
|
||||
layer.activeSubtab = function() {
|
||||
if (layer.subtabs[playerProxy.subtabs[layer.id].mainTabs] &&
|
||||
layer.subtabs[playerProxy.subtabs[layer.id].mainTabs].unlocked !== false) {
|
||||
return layer.subtabs[playerProxy.subtabs[layer.id].mainTabs];
|
||||
}
|
||||
// Default to first unlocked tab
|
||||
return Object.values(layer.subtabs).find(subtab => subtab.unlocked !== false);
|
||||
}
|
||||
setDefault(player, 'subtabs', {});
|
||||
setDefault(player.subtabs, layer.id, {});
|
||||
setDefault(player.subtabs[layer.id], 'mainTabs', Object.keys(layer.subtabs)[0]);
|
||||
for (let id in layer.subtabs) {
|
||||
if (isPlainObject(layer.subtabs[id])) {
|
||||
layer.subtabs[id].active = function() {
|
||||
return playerProxy.subtabs[this.layer].mainTabs === this.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layer.microtabs) {
|
||||
setDefault(player, 'subtabs', {});
|
||||
setDefault(player.subtabs, layer.id, {});
|
||||
for (let family in layer.microtabs) {
|
||||
layer.microtabs[family].activeMicrotab = function() {
|
||||
if (this[playerProxy.subtabs[this.layer][family]] && this[playerProxy.subtabs[this.layer][family]].unlocked !== false) {
|
||||
return this[playerProxy.subtabs[this.layer][family]];
|
||||
}
|
||||
// Default to first unlocked tab
|
||||
return this[Object.keys(this).find(microtab => microtab !== 'activeMicrotab' && this[microtab].unlocked !== false)];
|
||||
}
|
||||
setDefault(player.subtabs[layer.id], family, Object.keys(layer.microtabs[family]).find(tab => tab !== 'activeMicrotab'));
|
||||
layer.microtabs[family].layer = layer.id;
|
||||
layer.microtabs[family].family = family;
|
||||
for (let id in layer.microtabs[family]) {
|
||||
if (isPlainObject(layer.microtabs[family][id])) {
|
||||
layer.microtabs[family][id].layer = layer.id;
|
||||
layer.microtabs[family][id].family = family;
|
||||
layer.microtabs[family][id].id = id;
|
||||
layer.microtabs[family][id].active = function() {
|
||||
return playerProxy.subtabs[this.layer][this.family] === this.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layer.hotkeys) {
|
||||
for (let id in layer.hotkeys) {
|
||||
if (isPlainObject(layer.hotkeys[id])) {
|
||||
setDefault(layer.hotkeys[id], 'press', null, false);
|
||||
setDefault(layer.hotkeys[id], 'unlocked', function() {
|
||||
return layer.unlocked;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create layer proxy
|
||||
layer = createProxy(layer);
|
||||
|
||||
// Register layer
|
||||
layers[layer.id] = layer;
|
||||
|
||||
// Register hotkeys
|
||||
if (layer.hotkeys) {
|
||||
for (let id in layer.hotkeys) {
|
||||
hotkeys[layer.hotkeys[id].key] = layer.hotkeys[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function removeLayer(layer) {
|
||||
// Un-set hotkeys
|
||||
if (layers[layer].hotkeys) {
|
||||
for (let id in layers[layer].hotkeys) {
|
||||
delete hotkeys[id];
|
||||
}
|
||||
}
|
||||
|
||||
delete layers[layer];
|
||||
}
|
||||
|
||||
export function reloadLayer(layer) {
|
||||
removeLayer(layer.id);
|
||||
|
||||
// Re-create layer
|
||||
addLayer(layer);
|
||||
}
|
||||
|
||||
const uncachedProperties = [ 'startData', 'click', 'update', 'reset', 'hardReset' ];
|
||||
const gridProperties = [ 'upgrades', 'achievements', 'challenges', 'buyables', 'clickables' ];
|
||||
const featureProperties = [ 'upgrades', 'achievements', 'challenges', 'buyables', 'clickables', 'milestones', 'bars',
|
||||
'infoboxes', 'grids', 'hotkeys', 'subtabs' ];
|
||||
|
||||
function setRowCol(features) {
|
||||
if (features.rows && features.cols) {
|
||||
return
|
||||
}
|
||||
let maxRow = 0;
|
||||
let maxCol = 0;
|
||||
for (let id in features) {
|
||||
if (!isNaN(id)) {
|
||||
if (Math.floor(id / 10) > maxRow) {
|
||||
maxRow = Math.floor(id / 10);
|
||||
}
|
||||
if (id % 10 > maxCol) {
|
||||
maxCol = id % 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
features.rows = maxRow;
|
||||
features.cols = maxCol;
|
||||
}
|
||||
|
||||
function setupFeature(layer, features) {
|
||||
features.layer = layer;
|
||||
for (let id in features) {
|
||||
const feature = features[id];
|
||||
if (isPlainObject(feature)) {
|
||||
feature.id = id;
|
||||
feature.layer = layer;
|
||||
if (feature.unlocked == undefined) {
|
||||
feature.unlocked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setDefault(object, key, value, forceCached) {
|
||||
if (object[key] == undefined && value != undefined) {
|
||||
object[key] = value;
|
||||
}
|
||||
if (object[key] != undefined && isFunction(object[key]) && forceCached != undefined) {
|
||||
object[key].forceCached = forceCached;
|
||||
}
|
||||
}
|
604
src/game/layers.ts
Normal file
604
src/game/layers.ts
Normal file
|
@ -0,0 +1,604 @@
|
|||
import { CacheableFunction } from "@/typings/cacheableFunction";
|
||||
import { Achievement } from "@/typings/features/achievement";
|
||||
import { Buyable } from "@/typings/features/buyable";
|
||||
import { Challenge } from "@/typings/features/challenge";
|
||||
import { Clickable } from "@/typings/features/clickable";
|
||||
import {
|
||||
Feature,
|
||||
Features,
|
||||
GridFeatures,
|
||||
RawFeature,
|
||||
RawFeatures,
|
||||
RawGridFeatures
|
||||
} from "@/typings/features/feature";
|
||||
import { Grid } from "@/typings/features/grid";
|
||||
import { Hotkey } from "@/typings/features/hotkey";
|
||||
import { Milestone } from "@/typings/features/milestone";
|
||||
import { Microtab, Subtab } from "@/typings/features/subtab";
|
||||
import { Upgrade } from "@/typings/features/upgrade";
|
||||
import { Layer, RawLayer } from "@/typings/layer";
|
||||
import { PlayerData } from "@/typings/player";
|
||||
import { State } from "@/typings/state";
|
||||
import Decimal, { DecimalSource } from "@/util/bignum";
|
||||
import { isFunction } from "@/util/common";
|
||||
import {
|
||||
defaultLayerProperties,
|
||||
getStartingBuyables,
|
||||
getStartingChallenges,
|
||||
getStartingClickables,
|
||||
noCache
|
||||
} from "@/util/layers";
|
||||
import { createGridProxy, createLayerProxy } from "@/util/proxies";
|
||||
import { applyPlayerData } from "@/util/save";
|
||||
import clone from "lodash.clonedeep";
|
||||
import { isRef } from "vue";
|
||||
import { default as playerProxy } from "./player";
|
||||
|
||||
export const layers: Record<string, Readonly<Layer>> = {};
|
||||
export const hotkeys: Hotkey[] = [];
|
||||
window.layers = layers;
|
||||
|
||||
export function addLayer(layer: RawLayer, player?: Partial<PlayerData>): void {
|
||||
player = player || playerProxy;
|
||||
|
||||
// Check for required properties
|
||||
if (!("id" in layer)) {
|
||||
console.error(`Cannot add layer without a "id" property!`, layer);
|
||||
return;
|
||||
}
|
||||
if (layer.type === "static" || layer.type === "normal") {
|
||||
const missingProperty = ["baseAmount", "requires"].find(prop => !(prop in layer));
|
||||
if (missingProperty) {
|
||||
console.error(`Cannot add layer without a "${missingProperty}" property!`, layer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Clone object to prevent modifying the original
|
||||
layer = clone(layer);
|
||||
|
||||
setDefault(player, "layers", {});
|
||||
player.layers![layer.id] = applyPlayerData(
|
||||
{
|
||||
points: new Decimal(0),
|
||||
unlocked: false,
|
||||
resetTime: new Decimal(0),
|
||||
upgrades: [],
|
||||
achievements: [],
|
||||
milestones: [],
|
||||
infoboxes: {},
|
||||
buyables: getStartingBuyables(layer.buyables?.data),
|
||||
clickables: getStartingClickables(layer.clickables?.data),
|
||||
challenges: getStartingChallenges(layer.challenges?.data),
|
||||
grids: {},
|
||||
confirmRespecBuyables: false,
|
||||
...(layer.startData?.() || {})
|
||||
},
|
||||
player.layers![layer.id]
|
||||
);
|
||||
|
||||
// Set default property values
|
||||
layer = Object.assign({}, defaultLayerProperties, layer);
|
||||
layer.layer = layer.id;
|
||||
if (layer.type === "static" && layer.base == undefined) {
|
||||
layer.base = 2;
|
||||
}
|
||||
|
||||
// Process each feature
|
||||
const uncachedProperties = ["startData", "click", "update", "reset", "hardReset"];
|
||||
for (const property of uncachedProperties) {
|
||||
if (layer[property] && !isRef(layer.property) && isFunction(layer[property])) {
|
||||
(layer[property] as CacheableFunction).forceCached = false;
|
||||
}
|
||||
}
|
||||
if (layer.upgrades) {
|
||||
setupFeatures<
|
||||
RawGridFeatures<GridFeatures<Upgrade>, Upgrade>,
|
||||
GridFeatures<Upgrade>,
|
||||
Upgrade
|
||||
>(layer.id, layer.upgrades!);
|
||||
setRowCol(layer.upgrades);
|
||||
for (const id in layer.upgrades.data) {
|
||||
layer.upgrades.data[id].bought = function() {
|
||||
return (
|
||||
!layers[this.layer].deactivated &&
|
||||
playerProxy.layers[this.layer].upgrades.some(
|
||||
(upgrade: string | number) => upgrade == id
|
||||
)
|
||||
);
|
||||
};
|
||||
setDefault(layer.upgrades.data[id], "canAfford", function() {
|
||||
if (this.currencyInternalName) {
|
||||
const name = this.currencyInternalName;
|
||||
if (this.currencyLocation) {
|
||||
return !Decimal.lt(this.currencyLocation[name], this.cost);
|
||||
} else if (this.currencyLayer) {
|
||||
return !Decimal.lt(
|
||||
playerProxy.layers[this.currencyLayer][name] as DecimalSource,
|
||||
this.cost
|
||||
);
|
||||
} else {
|
||||
return !Decimal.lt(playerProxy[name] as DecimalSource, this.cost);
|
||||
}
|
||||
} else {
|
||||
return !playerProxy.layers[this.layer].points.lt(this.cost);
|
||||
}
|
||||
});
|
||||
setDefault(
|
||||
layer.upgrades.data[id],
|
||||
"pay",
|
||||
function() {
|
||||
if (this.bought || !this.canAfford) {
|
||||
return;
|
||||
}
|
||||
if (this.currencyInternalName) {
|
||||
const name = this.currencyInternalName;
|
||||
if (this.currencyLocation) {
|
||||
if (Decimal.lt(this.currencyLocation[name], this.cost)) {
|
||||
return;
|
||||
}
|
||||
this.currencyLocation[name] = Decimal.sub(
|
||||
this.currencyLocation[name],
|
||||
this.cost
|
||||
);
|
||||
} else if (this.currencyLayer) {
|
||||
const lr = this.currencyLayer;
|
||||
if (
|
||||
Decimal.lt(playerProxy.layers[lr][name] as DecimalSource, this.cost)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
playerProxy.layers[lr][name] = Decimal.sub(
|
||||
playerProxy.layers[lr][name] as DecimalSource,
|
||||
this.cost
|
||||
);
|
||||
} else {
|
||||
if (Decimal.lt(playerProxy[name] as DecimalSource, this.cost)) {
|
||||
return;
|
||||
}
|
||||
playerProxy[name] = Decimal.sub(
|
||||
playerProxy[name] as DecimalSource,
|
||||
this.cost
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (playerProxy.layers[this.layer].points.lt(this.cost)) {
|
||||
return;
|
||||
}
|
||||
playerProxy.layers[this.layer].points = playerProxy.layers[
|
||||
this.layer
|
||||
].points.sub(this.cost);
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
setDefault(
|
||||
layer.upgrades.data[id],
|
||||
"buy",
|
||||
function() {
|
||||
if (this.bought || !this.canAfford) {
|
||||
return;
|
||||
}
|
||||
this.pay();
|
||||
playerProxy.layers[this.layer].upgrades.push(this.id);
|
||||
this.onPurchase?.();
|
||||
},
|
||||
false
|
||||
);
|
||||
setDefault(layer.upgrades.data[id], "onPurchase", undefined, false);
|
||||
}
|
||||
}
|
||||
if (layer.achievements) {
|
||||
setupFeatures<
|
||||
RawGridFeatures<GridFeatures<Achievement>, Achievement>,
|
||||
GridFeatures<Achievement>,
|
||||
Achievement
|
||||
>(layer.id, layer.achievements!);
|
||||
setRowCol(layer.achievements);
|
||||
for (const id in layer.achievements.data) {
|
||||
layer.achievements.data[id].earned = function() {
|
||||
return (
|
||||
!layers[this.layer].deactivated &&
|
||||
playerProxy.layers[this.layer].achievements.some(
|
||||
(achievement: string | number) => achievement == id
|
||||
)
|
||||
);
|
||||
};
|
||||
setDefault(layer.achievements.data[id], "onComplete", undefined, false);
|
||||
}
|
||||
}
|
||||
if (layer.challenges) {
|
||||
setupFeatures<
|
||||
RawGridFeatures<GridFeatures<Challenge>, Challenge>,
|
||||
GridFeatures<Challenge>,
|
||||
Challenge
|
||||
>(layer.id, layer.challenges);
|
||||
setRowCol(layer.challenges);
|
||||
layer.activeChallenge = function() {
|
||||
return Object.values(this.challenges!.data).find(
|
||||
(challenge: Challenge) => challenge.active
|
||||
);
|
||||
};
|
||||
for (const id in layer.challenges.data) {
|
||||
layer.challenges.data[id].shown = function() {
|
||||
return (
|
||||
this.unlocked !== false && (playerProxy.hideChallenges === false || !this.maxed)
|
||||
);
|
||||
};
|
||||
layer.challenges.data[id].completed = function() {
|
||||
return (
|
||||
!layers[this.layer].deactivated &&
|
||||
playerProxy.layers[this.layer].challenges[id]?.gt(0)
|
||||
);
|
||||
};
|
||||
layer.challenges.data[id].completions = function() {
|
||||
return playerProxy.layers[this.layer].challenges[id];
|
||||
};
|
||||
layer.challenges.data[id].maxed = function() {
|
||||
return (
|
||||
!layers[this.layer].deactivated &&
|
||||
Decimal.gte(playerProxy.layers[this.layer].challenges[id], this.completionLimit)
|
||||
);
|
||||
};
|
||||
layer.challenges.data[id].active = function() {
|
||||
return (
|
||||
!layers[this.layer].deactivated &&
|
||||
playerProxy.layers[this.layer].activeChallenge === id
|
||||
);
|
||||
};
|
||||
layer.challenges.data[id].toggle = noCache(function(this: Challenge) {
|
||||
const exiting = playerProxy.layers[this.layer].activeChallenge === id;
|
||||
if (exiting) {
|
||||
if (this.canComplete && !this.maxed) {
|
||||
let completions: boolean | DecimalSource = this.canComplete;
|
||||
if (completions === true) {
|
||||
completions = 1;
|
||||
}
|
||||
playerProxy.layers[this.layer].challenges[id] = Decimal.min(
|
||||
playerProxy.layers[this.layer].challenges[id].add(completions),
|
||||
this.completionLimit
|
||||
);
|
||||
this.onComplete?.();
|
||||
}
|
||||
playerProxy.layers[this.layer].activeChallenge = null;
|
||||
this.onExit?.();
|
||||
layers[this.layer].reset(true);
|
||||
} else if (!exiting && this.canStart) {
|
||||
layers[this.layer].reset(true);
|
||||
playerProxy.layers[this.layer].activeChallenge = id;
|
||||
this.onEnter?.();
|
||||
}
|
||||
});
|
||||
setDefault(layer.challenges.data[id], "onComplete", undefined, false);
|
||||
setDefault(layer.challenges.data[id], "onEnter", undefined, false);
|
||||
setDefault(layer.challenges.data[id], "onExit", undefined, false);
|
||||
setDefault(layer.challenges.data[id], "canStart", true);
|
||||
setDefault(layer.challenges.data[id], "completionLimit", new Decimal(1));
|
||||
setDefault(layer.challenges.data[id], "mark", function() {
|
||||
return Decimal.gt(this.completionLimit, 1) && this.maxed;
|
||||
});
|
||||
setDefault(layer.challenges.data[id], "canComplete", function() {
|
||||
if (!this.active) {
|
||||
return false;
|
||||
}
|
||||
if (this.currencyInternalName) {
|
||||
const name = this.currencyInternalName;
|
||||
if (this.currencyLocation) {
|
||||
return !Decimal.lt(this.currencyLocation[name], this.goal);
|
||||
} else if (this.currencyLayer) {
|
||||
const lr = this.currencyLayer;
|
||||
return !Decimal.lt(
|
||||
playerProxy.layers[lr][name] as DecimalSource,
|
||||
this.goal
|
||||
);
|
||||
} else {
|
||||
return !Decimal.lt(playerProxy[name] as DecimalSource, this.goal);
|
||||
}
|
||||
} else {
|
||||
return !playerProxy.points.lt(this.goal);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (layer.buyables) {
|
||||
setupFeatures<
|
||||
RawGridFeatures<GridFeatures<Buyable>, Buyable>,
|
||||
GridFeatures<Buyable>,
|
||||
Buyable
|
||||
>(layer.id, layer.buyables);
|
||||
setRowCol(layer.buyables);
|
||||
setDefault(layer.buyables, "respec", undefined, false);
|
||||
setDefault(
|
||||
layer.buyables,
|
||||
"reset",
|
||||
function(this: NonNullable<Layer["buyables"]>) {
|
||||
playerProxy.layers[this.layer].buyables = getStartingBuyables(layer.buyables?.data);
|
||||
},
|
||||
false
|
||||
);
|
||||
for (const id in layer.buyables.data) {
|
||||
layer.buyables.data[id].amount = function() {
|
||||
return playerProxy.layers[this.layer].buyables[id];
|
||||
};
|
||||
layer.buyables.data[id].amountSet = function(amount: Decimal) {
|
||||
playerProxy.layers[this.layer].buyables[id] = amount;
|
||||
};
|
||||
layer.buyables.data[id].canBuy = function() {
|
||||
return (
|
||||
!layers[this.layer].deactivated &&
|
||||
this.unlocked !== false &&
|
||||
this.canAfford !== false &&
|
||||
Decimal.lt(playerProxy.layers[this.layer].buyables[id], this.purchaseLimit)
|
||||
);
|
||||
};
|
||||
setDefault(layer.buyables.data[id], "purchaseLimit", new Decimal(Infinity));
|
||||
setDefault(layer.buyables.data[id], "sellOne", undefined, false);
|
||||
setDefault(layer.buyables.data[id], "sellAll", undefined, false);
|
||||
if (layer.buyables.data[id].cost != undefined) {
|
||||
setDefault(
|
||||
layer.buyables.data[id],
|
||||
"buy",
|
||||
function() {
|
||||
if (this.canBuy) {
|
||||
playerProxy.layers[this.layer].points = playerProxy.layers[
|
||||
this.layer
|
||||
].points.sub(this.cost!);
|
||||
this.amount = this.amount.add(1);
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layer.clickables) {
|
||||
setupFeatures<
|
||||
RawGridFeatures<GridFeatures<Clickable>, Clickable>,
|
||||
GridFeatures<Clickable>,
|
||||
Clickable
|
||||
>(layer.id, layer.clickables);
|
||||
setRowCol(layer.clickables);
|
||||
setDefault(layer.clickables, "masterButtonClick", undefined, false);
|
||||
if (layer.clickables.masterButtonDisplay != undefined) {
|
||||
setDefault(layer.clickables, "showMasterButton", true);
|
||||
}
|
||||
for (const id in layer.clickables.data) {
|
||||
layer.clickables.data[id].state = function() {
|
||||
return playerProxy.layers[this.layer].clickables[id];
|
||||
};
|
||||
layer.clickables.data[id].stateSet = function(state: State) {
|
||||
playerProxy.layers[this.layer].clickables[id] = state;
|
||||
};
|
||||
setDefault(layer.clickables.data[id], "canClick", true);
|
||||
setDefault(layer.clickables.data[id], "click", undefined, false);
|
||||
setDefault(layer.clickables.data[id], "hold", undefined, false);
|
||||
}
|
||||
}
|
||||
if (layer.milestones) {
|
||||
setupFeatures<RawFeatures<Features<Milestone>, Milestone>, Features<Milestone>, Milestone>(
|
||||
layer.id,
|
||||
layer.milestones
|
||||
);
|
||||
for (const id in layer.milestones.data) {
|
||||
layer.milestones.data[id].earned = function() {
|
||||
return (
|
||||
!layer.deactivated &&
|
||||
playerProxy.layers[this.layer].milestones.some(
|
||||
(milestone: string | number) => milestone == id
|
||||
)
|
||||
);
|
||||
};
|
||||
layer.milestones.data[id].shown = function() {
|
||||
if (!this.unlocked) {
|
||||
return false;
|
||||
}
|
||||
switch (playerProxy.msDisplay) {
|
||||
default:
|
||||
case "all":
|
||||
return true;
|
||||
case "last":
|
||||
return (
|
||||
this.optionsDisplay ||
|
||||
!this.earned ||
|
||||
playerProxy.layers[this.layer].milestones[
|
||||
playerProxy.layers[this.layer].milestones.length - 1
|
||||
] === this.id
|
||||
);
|
||||
case "configurable":
|
||||
return this.optionsDisplay || !this.earned;
|
||||
case "incomplete":
|
||||
return !this.earned;
|
||||
case "none":
|
||||
return false;
|
||||
}
|
||||
};
|
||||
setDefault(layer.milestones.data[id], "done", false);
|
||||
}
|
||||
}
|
||||
if (layer.grids) {
|
||||
setupFeatures<RawFeatures<Features<Grid>, Grid>, Features<Grid>, Grid>(
|
||||
layer.id,
|
||||
layer.grids
|
||||
);
|
||||
for (const id in layer.grids.data) {
|
||||
setDefault(player.layers![layer.id].grids, id, {});
|
||||
layer.grids.data[id].getData = function(cell): State {
|
||||
if (playerProxy.layers[this.layer].grids[id][cell] != undefined) {
|
||||
return playerProxy.layers[this.layer].grids[id][cell];
|
||||
}
|
||||
if (isFunction(this.getStartData)) {
|
||||
return (this.getStartData as (this: Grid, cell: string | number) => State)(
|
||||
cell
|
||||
);
|
||||
}
|
||||
return this.getStartData;
|
||||
};
|
||||
layer.grids.data[id].setData = function(cell, data) {
|
||||
playerProxy.layers[this.layer].grids[id][cell] = data;
|
||||
};
|
||||
setDefault(layer.grids.data[id], "getUnlocked", true, false);
|
||||
setDefault(layer.grids.data[id], "getCanClick", true, false);
|
||||
setDefault(layer.grids.data[id], "getStartData", "", false);
|
||||
setDefault(layer.grids.data[id], "getStyle", undefined, false);
|
||||
setDefault(layer.grids.data[id], "click", undefined, false);
|
||||
setDefault(layer.grids.data[id], "hold", undefined, false);
|
||||
setDefault(layer.grids.data[id], "getTitle", undefined, false);
|
||||
layer.grids.data[id] = createGridProxy(layer.grids.data[id]) as Grid;
|
||||
}
|
||||
}
|
||||
if (layer.subtabs) {
|
||||
layer.activeSubtab = function() {
|
||||
if (
|
||||
layers[this.layer].subtabs![playerProxy.subtabs[this.layer].mainTabs!] &&
|
||||
layers[this.layer].subtabs![playerProxy.subtabs[this.layer].mainTabs!].unlocked !==
|
||||
false
|
||||
) {
|
||||
return layers[this.layer].subtabs![playerProxy.subtabs[this.layer].mainTabs!];
|
||||
}
|
||||
// Default to first unlocked tab
|
||||
return Object.values(layers[this.layer].subtabs!).find(
|
||||
(subtab: Subtab) => subtab.unlocked !== false
|
||||
);
|
||||
};
|
||||
setDefault(player, "subtabs", {});
|
||||
setDefault(player.subtabs!, layer.id, {});
|
||||
setDefault(player.subtabs![layer.id], "mainTabs", Object.keys(layer.subtabs)[0]);
|
||||
for (const id in layer.subtabs) {
|
||||
layer.subtabs[id].active = function() {
|
||||
return playerProxy.subtabs[this.layer].mainTabs === this.id;
|
||||
};
|
||||
}
|
||||
}
|
||||
if (layer.microtabs) {
|
||||
setDefault(player, "subtabs", {});
|
||||
setDefault(player.subtabs!, layer.id, {});
|
||||
for (const family in layer.microtabs) {
|
||||
if (Object.keys(layer.microtabs[family]).length === 0) {
|
||||
console.warn(
|
||||
"Cannot create microtab family with 0 tabs",
|
||||
layer.id,
|
||||
family,
|
||||
layer.microtabs[family]
|
||||
);
|
||||
continue;
|
||||
}
|
||||
layer.microtabs[family].activeMicrotab = function() {
|
||||
if (
|
||||
this.data[playerProxy.subtabs[this.layer as string][family]] &&
|
||||
this.data[playerProxy.subtabs[this.layer as string][family]].unlocked !== false
|
||||
) {
|
||||
return this[playerProxy.subtabs[this.layer as string][family]];
|
||||
}
|
||||
// Default to first unlocked tab
|
||||
const firstUnlocked: string | undefined = Object.keys(this).find(
|
||||
microtab =>
|
||||
microtab !== "activeMicrotab" && this.data[microtab].unlocked !== false
|
||||
);
|
||||
return firstUnlocked != undefined ? this[firstUnlocked] : undefined;
|
||||
};
|
||||
setDefault(
|
||||
player.subtabs![layer.id],
|
||||
family,
|
||||
Object.keys(layer.microtabs[family]).find(tab => tab !== "activeMicrotab")!
|
||||
);
|
||||
layer.microtabs[family].layer = layer.id;
|
||||
layer.microtabs[family].family = family;
|
||||
for (const id in layer.microtabs[family].data) {
|
||||
const microtab: RawFeature<Microtab> = layer.microtabs[family].data[id];
|
||||
microtab.layer = layer.id;
|
||||
microtab.family = family;
|
||||
microtab.id = id;
|
||||
microtab.active = function() {
|
||||
return playerProxy.subtabs[this.layer][this.family] === this.id;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layer.hotkeys) {
|
||||
for (const id in layer.hotkeys) {
|
||||
setDefault(layer.hotkeys[id], "press", undefined, false);
|
||||
setDefault(layer.hotkeys[id], "unlocked", function() {
|
||||
return layers[this.layer].unlocked;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Create layer proxy
|
||||
layer = createLayerProxy(layer) as Layer;
|
||||
|
||||
// Register layer
|
||||
layers[layer.id] = layer as Layer;
|
||||
|
||||
// Register hotkeys
|
||||
if (layers[layer.id].hotkeys) {
|
||||
for (const hotkey of layers[layer.id].hotkeys!) {
|
||||
hotkeys.push(hotkey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function removeLayer(layer: string): void {
|
||||
// Un-set hotkeys
|
||||
if (layers[layer].hotkeys) {
|
||||
for (const hotkey of Object.values(layers[layer].hotkeys!)) {
|
||||
const index = hotkeys.indexOf(hotkey);
|
||||
if (index >= 0) {
|
||||
hotkeys.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete layers[layer];
|
||||
}
|
||||
|
||||
export function reloadLayer(layer: Layer): void {
|
||||
removeLayer(layer.id);
|
||||
|
||||
// Re-create layer
|
||||
addLayer(layer);
|
||||
}
|
||||
|
||||
function setRowCol<T extends GridFeatures<S>, S extends Feature>(features: RawGridFeatures<T, S>) {
|
||||
if (features.rows && features.cols) {
|
||||
return;
|
||||
}
|
||||
let maxRow = 0;
|
||||
let maxCol = 0;
|
||||
for (const id in features) {
|
||||
const index = Number(id);
|
||||
if (!isNaN(index)) {
|
||||
if (Math.floor(index / 10) > maxRow) {
|
||||
maxRow = Math.floor(index / 10);
|
||||
}
|
||||
if (index % 10 > maxCol) {
|
||||
maxCol = index % 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
features.rows = maxRow;
|
||||
features.cols = maxCol;
|
||||
}
|
||||
|
||||
function setupFeatures<T extends RawFeatures<R, S>, R extends Features<S>, S extends Feature>(
|
||||
layer: string,
|
||||
features: T
|
||||
) {
|
||||
features.layer = layer;
|
||||
for (const id in features.data) {
|
||||
const feature = features.data[id];
|
||||
(feature as Feature).id = id;
|
||||
(feature as Feature).layer = layer;
|
||||
if (feature.unlocked == undefined) {
|
||||
(feature as Feature).unlocked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setDefault<T, K extends keyof T>(object: T, key: K, value: T[K], forceCached?: boolean) {
|
||||
if (object[key] == undefined && value != undefined) {
|
||||
object[key] = value;
|
||||
}
|
||||
if (object[key] != undefined && isFunction(object[key]) && forceCached != undefined) {
|
||||
Object.assign(object[key], { forceCached });
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
import { reactive } from 'vue';
|
||||
import { isPlainObject } from '../util/common';
|
||||
import Decimal from '../util/bignum';
|
||||
|
||||
const state = reactive({});
|
||||
|
||||
const playerHandler = {
|
||||
get(target, key) {
|
||||
if (key === '__state' || key === '__path') {
|
||||
return target[key];
|
||||
}
|
||||
if (target.__state[key] == undefined) {
|
||||
return;
|
||||
}
|
||||
if (isPlainObject(target.__state[key]) && !(target.__state[key] instanceof Decimal)) {
|
||||
if (target.__state[key] !== target[key]?.__state) {
|
||||
const path = [ ...target.__path, key ];
|
||||
target[key] = new Proxy({ __state: target.__state[key], __path: path }, playerHandler);
|
||||
}
|
||||
return target[key];
|
||||
}
|
||||
|
||||
return target.__state[key];
|
||||
},
|
||||
set(target, property, value, receiver) {
|
||||
if (!state.hasNaN && ((typeof value === 'number' && isNaN(value)) || (value instanceof Decimal && (isNaN(value.sign) || isNaN(value.layer) || isNaN(value.mag))))) {
|
||||
const currentValue = target.__state[property];
|
||||
if (!((typeof currentValue === 'number' && isNaN(currentValue)) || (currentValue instanceof Decimal && (isNaN(currentValue.sign) || isNaN(currentValue.layer) || isNaN(currentValue.mag))))) {
|
||||
state.autosave = false;
|
||||
state.hasNaN = true;
|
||||
state.NaNPath = [ ...target.__path, property ];
|
||||
state.NaNReceiver = receiver;
|
||||
console.error(`Attempted to set NaN value`, [ ...target.__path, property ], target.__state);
|
||||
throw 'Attempted to set NaN value. See above for details';
|
||||
}
|
||||
}
|
||||
target.__state[property] = value;
|
||||
if (property === 'points') {
|
||||
if (target.__state.best != undefined) {
|
||||
target.__state.best = Decimal.max(target.__state.best, value);
|
||||
}
|
||||
if (target.__state.total != undefined) {
|
||||
const diff = Decimal.sub(value, target.__state.points);
|
||||
if (diff.gt(0)) {
|
||||
target.__state.total = target.__state.total.add(diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
export default window.player = new Proxy({ __state: state, __path: [ 'player' ] }, playerHandler);
|
112
src/game/player.ts
Normal file
112
src/game/player.ts
Normal file
|
@ -0,0 +1,112 @@
|
|||
import { Themes } from "@/data/themes";
|
||||
import { PlayerData } from "@/typings/player";
|
||||
import Decimal from "@/util/bignum";
|
||||
import { isPlainObject } from "@/util/common";
|
||||
import { reactive } from "vue";
|
||||
import { ImportingStatus, MilestoneDisplay } from "./enums";
|
||||
|
||||
const state = reactive<PlayerData>({
|
||||
id: "",
|
||||
points: new Decimal(0),
|
||||
oomps: new Decimal(0),
|
||||
oompsMag: 0,
|
||||
name: "",
|
||||
tabs: [],
|
||||
time: -1,
|
||||
autosave: true,
|
||||
offlineProd: true,
|
||||
offlineTime: null,
|
||||
timePlayed: new Decimal(0),
|
||||
keepGoing: false,
|
||||
lastTenTicks: [],
|
||||
showTPS: true,
|
||||
msDisplay: MilestoneDisplay.All,
|
||||
hideChallenges: false,
|
||||
theme: Themes.Paper,
|
||||
subtabs: {},
|
||||
minimized: {},
|
||||
modID: "",
|
||||
modVersion: "",
|
||||
hasNaN: false,
|
||||
NaNPath: [],
|
||||
NaNReceiver: null,
|
||||
importing: ImportingStatus.NotImporting,
|
||||
saveToImport: "",
|
||||
saveToExport: "",
|
||||
layers: {}
|
||||
});
|
||||
|
||||
const playerHandler: ProxyHandler<Record<string, any>> = {
|
||||
get(target: Record<string, any>, key: string): any {
|
||||
if (key === "__state" || key === "__path") {
|
||||
return target[key];
|
||||
}
|
||||
if (target.__state[key] == undefined) {
|
||||
return;
|
||||
}
|
||||
if (isPlainObject(target.__state[key]) && !(target.__state[key] instanceof Decimal)) {
|
||||
if (target.__state[key] !== target[key]?.__state) {
|
||||
const path = [...target.__path, key];
|
||||
target[key] = new Proxy(
|
||||
{ __state: target.__state[key], __path: path },
|
||||
playerHandler
|
||||
);
|
||||
}
|
||||
return target[key];
|
||||
}
|
||||
|
||||
return target.__state[key];
|
||||
},
|
||||
set(
|
||||
target: Record<string, any>,
|
||||
property: string,
|
||||
value: any,
|
||||
receiver: ProxyConstructor
|
||||
): boolean {
|
||||
if (
|
||||
!state.hasNaN &&
|
||||
((typeof value === "number" && isNaN(value)) ||
|
||||
(value instanceof Decimal &&
|
||||
(isNaN(value.sign) || isNaN(value.layer) || isNaN(value.mag))))
|
||||
) {
|
||||
const currentValue = target.__state[property];
|
||||
if (
|
||||
!(
|
||||
(typeof currentValue === "number" && isNaN(currentValue)) ||
|
||||
(currentValue instanceof Decimal &&
|
||||
(isNaN(currentValue.sign) ||
|
||||
isNaN(currentValue.layer) ||
|
||||
isNaN(currentValue.mag)))
|
||||
)
|
||||
) {
|
||||
state.autosave = false;
|
||||
state.hasNaN = true;
|
||||
state.NaNPath = [...target.__path, property];
|
||||
state.NaNReceiver = (receiver as unknown) as Record<string, unknown>;
|
||||
console.error(
|
||||
`Attempted to set NaN value`,
|
||||
[...target.__path, property],
|
||||
target.__state
|
||||
);
|
||||
throw "Attempted to set NaN value. See above for details";
|
||||
}
|
||||
}
|
||||
target.__state[property] = value;
|
||||
if (property === "points") {
|
||||
if (target.__state.best != undefined) {
|
||||
target.__state.best = Decimal.max(target.__state.best, value);
|
||||
}
|
||||
if (target.__state.total != undefined) {
|
||||
const diff = Decimal.sub(value, target.__state.points);
|
||||
if (diff.gt(0)) {
|
||||
target.__state.total = target.__state.total.add(diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
export default window.player = new Proxy(
|
||||
{ __state: state, __path: ["player"] },
|
||||
playerHandler
|
||||
) as PlayerData;
|
File diff suppressed because one or more lines are too long
2717
src/lib/break_eternity.ts
Normal file
2717
src/lib/break_eternity.ts
Normal file
File diff suppressed because it is too large
Load diff
22
src/main.js
22
src/main.js
|
@ -1,22 +0,0 @@
|
|||
import { createApp } from 'vue';
|
||||
import App from './App';
|
||||
import { load } from './util/save';
|
||||
import { setVue } from './util/vue';
|
||||
import gameLoop from './game/gameLoop';
|
||||
import { registerComponents } from './components/index';
|
||||
import modInfo from './data/modInfo.json';
|
||||
|
||||
requestAnimationFrame(async () => {
|
||||
await load();
|
||||
|
||||
// Create Vue
|
||||
const vue = window.vue = createApp({
|
||||
...App
|
||||
});
|
||||
setVue(vue);
|
||||
registerComponents(vue);
|
||||
vue.mount('#app');
|
||||
document.title = modInfo.title;
|
||||
|
||||
gameLoop();
|
||||
});
|
22
src/main.ts
Normal file
22
src/main.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import { load } from "./util/save";
|
||||
import { setVue } from "./util/vue";
|
||||
import gameLoop from "./game/gameLoop";
|
||||
import { registerComponents } from "./components/index";
|
||||
import modInfo from "./data/modInfo.json";
|
||||
|
||||
requestAnimationFrame(async () => {
|
||||
await load();
|
||||
|
||||
// Create Vue
|
||||
const vue = (window.vue = createApp({
|
||||
...App
|
||||
}));
|
||||
setVue(vue);
|
||||
registerComponents(vue);
|
||||
vue.mount("#app");
|
||||
document.title = modInfo.title;
|
||||
|
||||
gameLoop();
|
||||
});
|
6
src/shims-vue.d.ts
vendored
Normal file
6
src/shims-vue.d.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
/* eslint-disable */
|
||||
declare module '*.vue' {
|
||||
import type { defineComponent } from 'vue';
|
||||
const component: ReturnType<typeof defineComponent>;
|
||||
export default component;
|
||||
}
|
28
src/typings/branches.d.ts
vendored
Normal file
28
src/typings/branches.d.ts
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { ComponentPublicInstance } from "vue";
|
||||
|
||||
export interface BranchLink {
|
||||
start: string;
|
||||
end: string;
|
||||
options: string | BranchOptions;
|
||||
}
|
||||
|
||||
export interface BranchNode {
|
||||
x?: number;
|
||||
y?: number;
|
||||
component: ComponentPublicInstance;
|
||||
element: HTMLElement;
|
||||
}
|
||||
|
||||
export interface BranchOptions {
|
||||
target?: string;
|
||||
featureType?: string;
|
||||
stroke?: string;
|
||||
"stroke-width"?: string;
|
||||
startOffset?: Position;
|
||||
endOffset?: Position;
|
||||
}
|
||||
|
||||
export interface Position {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
3
src/typings/cacheableFunction.d.ts
vendored
Normal file
3
src/typings/cacheableFunction.d.ts
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
export interface CacheableFunction extends Function {
|
||||
forceCached?: boolean;
|
||||
}
|
3
src/typings/component.d.ts
vendored
Normal file
3
src/typings/component.d.ts
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { ComponentOptions } from "vue";
|
||||
|
||||
export type CoercableComponent = string | ComponentOptions;
|
5
src/typings/computable.d.ts
vendored
Normal file
5
src/typings/computable.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
export type Computable<T> = {
|
||||
[K in keyof T]:
|
||||
| ((this: T) => T[K])
|
||||
| (NonNullable<T[K]> extends (..._: infer A) => infer R ? (this: T, ..._: A) => R : T[K]);
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue