diff --git a/.eslintrc.json b/.eslintrc.json index b80c8fa3c..545ec5291 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,7 @@ { "extends": [ "eslint:recommended", + "plugin:import/recommended", "plugin:vue/recommended" ], "env": { @@ -14,7 +15,46 @@ "sourceType": "module", "parser": "@babel/eslint-parser" }, + "settings": { + "import/resolver": { + "alias": { + "map": [ + ["@", "./src"] + ], + "extensions": [".js", ".vue"] + } + } + }, "rules": { + "import/prefer-default-export": "off", + + "import/no-unresolved": "error", + "import/named": "error", + "import/namespace": "error", + "import/default": "error", + "import/export": "error", + "import/no-named-as-default": "warn", + "import/no-named-as-default-member": "warn", + "import/no-duplicates": "warn", + "import/extensions": ["warn", "never"], + "import/first": "warn", + "import/newline-after-import": "warn", + "import/no-named-default": "warn", + "import/no-self-import": "warn", + "import/order": [ + "warn", + { + "newlines-between": "always-and-inside-groups", + "pathGroups": [{ "pattern": "@/**", "group": "sibling" }] + } + ], + "sort-imports": [ + "warn", + { + "ignoreCase": true, + "allowSeparatedGroups": true + } + ], "no-console": "warn", "no-template-curly-in-string": "warn", "array-callback-return": "error", @@ -32,7 +72,6 @@ "allowElseIf": false } ], - "vue/one-component-per-file": "error", "vue/component-definition-name-casing": "warn", "vue/order-in-components": "warn", @@ -161,7 +200,6 @@ ], "new-parens": "error", "no-array-constructor": "warn", - "no-bitwise": "warn", "no-inline-comments": "error", "no-lonely-if": "error", "no-mixed-spaces-and-tabs": "error", diff --git a/.stylelintignore b/.stylelintignore new file mode 100644 index 000000000..8a73544d0 --- /dev/null +++ b/.stylelintignore @@ -0,0 +1,3 @@ +public/stylesheets/fontawesome/**/*.css +public/stylesheets/codemirror/*.css +public/stylesheets/vis-network.css diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 000000000..5920616c9 --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,425 @@ +{ + "plugins": [ + "stylelint-order" + ], + "extends": [ + "stylelint-config-standard" + ], + "rules": { + "no-descending-specificity": null, + "font-family-no-missing-generic-family-keyword": null, + "no-empty-source": null, + + "color-hex-length": "long", + "custom-property-empty-line-before": null, + "color-function-notation": null, + "declaration-empty-line-before": null, + "comment-empty-line-before": null, + + "property-no-vendor-prefix": [ + true, + { + "ignoreProperties": ["appearance", "background-clip", "backdrop-filter", "clip-path", "user-select"] + } + ], + + "unit-allowed-list": [ + "rem", + "%", + "px", + "deg", + "s", + "ms", + "fr" + ], + "custom-property-pattern": [ + "^([_a-z][a-z0-9]*)([-_]{1,2}[a-z0-9]+)*$", + { + "message": "Expected custom property name to be kebab-case" + } + ], + "selector-class-pattern": [ + "^(CodeMirror.*|([_a-z][a-z0-9]*)([-_]{1,2}[a-z0-9]+)*)$", + { + "message": "Expected class selector name to be kebab-case" + } + ], + "keyframes-name-pattern": [ + "^a-([-_]{0,2}[a-z0-9]+)*$", + { + "message": "Keyframe name must begin with `a-` and be kebab-case" + } + ], + "selector-id-pattern": [ + "^([_a-z][a-z0-9]*)([-_]{1,2}[a-z0-9]+)*$", + { + "message": "Expected id selector name to be kebab-case" + } + ], + + "order/order": [ + "custom-properties", + "at-rules", + "rules", + "declarations" + ], + "order/properties-order": [ + [ + { + "groupName": "content", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "content" + ] + }, + { + "groupName": "display", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "display", + "visibility", + "float", + "clear", + "resize", + "overflow", + "overflow-x", + "overflow-y", + "white-space", + "word-break", + "overflow-wrap", + "tab-size", + "clip", + "zoom" + ] + }, + { + "groupName": "flex", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "flex", + "flex-grow", + "flex-shrink", + "flex-basis", + "flex-flow", + "flex-direction", + "flex-wrap" + ] + }, + { + "groupName": "grid", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "grid", + "grid-auto-columns", + "grid-auto-flow", + "grid-auto-rows", + "grid-template-areas", + "grid-template-columns", + "grid-template-rows", + "grid-row-gap", + "grid-column-gap", + "row-gap", + "column-gap", + "grid-row", + "grid-row-start", + "grid-row-end", + "grid-column", + "grid-column-start", + "grid-column-end" + ] + }, + { + "groupName": "table", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "table-layout", + "empty-cells", + "caption-side", + "border-spacing", + "border-collapse", + "list-style", + "list-style-position", + "list-style-type", + "list-style-image" + ] + }, + { + "groupName": "size", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "width", + "height", + "min-width", + "max-width", + "min-height", + "max-height" + ] + }, + { + "groupName": "position", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "position", + "will-change", + "inset", + "top", + "right", + "bottom", + "left", + "z-index" + ] + }, + { + "groupName": "alignment", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "place-content", + "justify-content", + "align-content", + "align-items", + "align-self", + "vertical-align", + "text-align", + "text-align-last" + ] + }, + { + "groupName": "scrollbar", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "scrollbar-color", + "scrollbar-width" + ] + }, + { + "groupName": "svg", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "stroke", + "stroke-width", + "stroke-linecap", + "stroke-dasharray", + "fill", + "text-anchor" + ] + }, + { + "groupName": "font", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "font", + "font-family", + "font-size", + "font-stretch", + "font-style", + "font-variant", + "font-weight", + "font-smoothing", + "font-smooth", + "line-height", + "src", + "unicode-range" + ] + }, + { + "groupName": "color", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "opacity", + "color" + ] + }, + { + "groupName": "text", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "text-shadow", + "text-decoration" + ] + }, + "appearance", + { + "groupName": "background", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "background", + "background-attachment", + "background-clip", + "background-color", + "background-image", + "background-origin", + "background-position", + "background-position-x", + "background-position-y", + "background-repeat", + "background-size" + ] + }, + { + "groupName": "border", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "border", + "border-color", + "border-style", + "border-width", + "border-top", + "border-top-color", + "border-top-style", + "border-top-width", + "border-right", + "border-right-color", + "border-right-style", + "border-right-width", + "border-bottom", + "border-bottom-color", + "border-bottom-style", + "border-bottom-width", + "border-left", + "border-left-color", + "border-left-style", + "border-left-width", + "border-radius", + "border-top-left-radius", + "border-top-right-radius", + "border-bottom-right-radius", + "border-bottom-left-radius", + "border-spacing" + ] + }, + { + "groupName": "box", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "box-shadow", + "box-sizing" + ] + }, + { + "groupName": "outline", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "outline", + "outline-width", + "outline-style", + "outline-color", + "outline-offset" + ] + }, + { + "groupName": "margin", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "margin", + "margin-top", + "margin-right", + "margin-bottom", + "margin-left" + ] + }, + { + "groupName": "padding", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "padding", + "padding-top", + "padding-right", + "padding-bottom", + "padding-left" + ] + }, + { + "groupName": "animation", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "transform", + "transform-origin", + "filter", + "mix-blend-mode", + "transition", + "transition-delay", + "transition-timing-function", + "transition-duration", + "transition-property", + "animation", + "animation-name", + "animation-duration", + "animation-play-state", + "animation-timing-function", + "animation-delay", + "animation-iteration-count", + "animation-direction", + "animation-fill-mode" + ] + }, + { + "groupName": "pointer", + "emptyLineBefore": "never", + "noEmptyLineBetween": true, + "properties": [ + "pointer-events", + "user-select", + "cursor" + ] + } + ], + { + "unspecified": "bottomAlphabetical", + "emptyLineBeforeUnspecified": "always" + } + ] + }, + "overrides": [ + { + "files": [ + "*.vue", + "**/*.vue" + ], + "extends": [ + "stylelint-config-recommended", + "stylelint-config-html" + ], + "rules": { + "selector-pseudo-class-no-unknown": [ + true, + { + "ignorePseudoClasses": [ + "deep", + "global" + ] + } + ], + "selector-pseudo-element-no-unknown": [ + true, + { + "ignorePseudoElements": [ + "v-deep", + "v-global", + "v-slotted" + ] + } + ] + } + } + ] +} diff --git a/build/check-npm.js b/build/check-npm.js index 5933b575e..1843fcfbc 100644 --- a/build/check-npm.js +++ b/build/check-npm.js @@ -1,8 +1,10 @@ -/* eslint-disable no-bitwise */ +/* eslint-disable no-console */ const fs = require("fs"); const path = require("path"); const proc = require("child_process"); +const readline = require("readline"); + function getHash(string) { let hash = 0; @@ -31,9 +33,23 @@ if (newHash !== currentHash) { fs.mkdirSync(tmpPath); } - // eslint-disable-next-line no-console + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + console.log("package-lock.json changes were detected"); - console.log("Running 'npm ci' (this might take a while)..."); - proc.execSync("npm ci"); - fs.writeFileSync(hashPath, newHash, {}); + const timeout = setTimeout(() => { + rl.close(); + console.log("Running 'npm ci' (this might take a while)..."); + proc.execSync("npm ci"); + fs.writeFileSync(hashPath, newHash, {}); + }, 5000); + + // eslint-disable-next-line max-len + rl.question(`Press enter within the next five seconds to skip running 'npm ci' - this will leave your packages out of sync!`, () => { + console.log(`'npm ci' step skipped`); + rl.close(); + clearTimeout(timeout); + }); } diff --git a/javascripts/core/achievements/normal-achievement.js b/javascripts/core/achievements/normal-achievement.js index 5519be2dd..032b42961 100644 --- a/javascripts/core/achievements/normal-achievement.js +++ b/javascripts/core/achievements/normal-achievement.js @@ -1,13 +1,11 @@ -import { GameMechanicState } from "../game-mechanics/index.js"; +import { GameMechanicState } from "../game-mechanics/index"; class AchievementState extends GameMechanicState { constructor(config) { super(config); this._row = Math.floor(this.id / 10); this._column = this.id % 10; - // eslint-disable-next-line no-bitwise this._bitmask = 1 << (this.column - 1); - // eslint-disable-next-line no-bitwise this._inverseBitmask = ~this._bitmask; this.registerEvents(config.checkEvent, args => this.tryUnlock(args)); } @@ -33,7 +31,6 @@ class AchievementState extends GameMechanicState { } get isUnlocked() { - // eslint-disable-next-line no-bitwise return (player.achievementBits[this.row - 1] & this._bitmask) !== 0; } @@ -52,21 +49,22 @@ class AchievementState extends GameMechanicState { } lock() { - // eslint-disable-next-line no-bitwise player.achievementBits[this.row - 1] &= this._inverseBitmask; } unlock(auto) { if (this.isUnlocked) return; - // eslint-disable-next-line no-bitwise player.achievementBits[this.row - 1] |= this._bitmask; if (this.id === 85 || this.id === 93) { Autobuyer.bigCrunch.bumpAmount(4); } if (this.id === 55 && !PlayerProgress.realityUnlocked()) { - Modal.message.show(`Since you performed an Infinity in under a minute, the UI changed on the screen. - Instead of the Dimensions disappearing, they stay and the Big Crunch button appears on top of them. - This is purely visual, and is there to prevent flickering.`); + Modal.message.show(`Since you performed an Infinity in under a minute, the UI changed on the screen. + Instead of the Dimensions disappearing, they stay and the Big Crunch button appears on top of them. + This is purely visual, and is there to prevent flickering.`, {}, 3); + } + if (this.id === 148 || this.id === 166) { + GameCache.staticGlyphWeights.invalidate(); } if (auto) { GameUI.notify.reality(`Automatically unlocked: ${this.name}`); @@ -169,8 +167,7 @@ export const Achievements = { const unlockedRows = Achievements.allRows .countWhere(row => row.every(ach => ach.isUnlocked)); const basePower = Math.pow(1.25, unlockedRows) * Math.pow(1.03, Achievements.effectiveCount); - let exponent = getAdjustedGlyphEffect("effarigachievement"); - if (Ra.has(RA_UNLOCKS.ACHIEVEMENT_POW)) exponent *= 1.5; + const exponent = getAdjustedGlyphEffect("effarigachievement") * Ra.unlocks.achievementPower.effectOrDefault(1); return Math.pow(basePower, exponent); }), diff --git a/javascripts/core/achievements/secret-achievement.js b/javascripts/core/achievements/secret-achievement.js index 8d2ec2710..02a8189a6 100644 --- a/javascripts/core/achievements/secret-achievement.js +++ b/javascripts/core/achievements/secret-achievement.js @@ -1,13 +1,11 @@ -import { GameMechanicState } from "../game-mechanics/index.js"; +import { GameMechanicState } from "../game-mechanics/index"; class SecretAchievementState extends GameMechanicState { constructor(config) { super(config); this._row = Math.floor(this.id / 10); this._column = this.id % 10; - // eslint-disable-next-line no-bitwise this._bitmask = 1 << (this.column - 1); - // eslint-disable-next-line no-bitwise this._inverseBitmask = ~this._bitmask; this.registerEvents(config.checkEvent, args => this.tryUnlock(args)); } @@ -25,7 +23,6 @@ class SecretAchievementState extends GameMechanicState { } get isUnlocked() { - // eslint-disable-next-line no-bitwise return (player.secretAchievementBits[this.row - 1] & this._bitmask) !== 0; } @@ -37,14 +34,12 @@ class SecretAchievementState extends GameMechanicState { unlock() { if (this.isUnlocked) return; - // eslint-disable-next-line no-bitwise player.secretAchievementBits[this.row - 1] |= this._bitmask; GameUI.notify.success(`Secret Achievement: ${this.name}`); EventHub.dispatch(GAME_EVENT.ACHIEVEMENT_UNLOCKED); } lock() { - // eslint-disable-next-line no-bitwise player.secretAchievementBits[this.row - 1] &= this._inverseBitmask; } } diff --git a/javascripts/core/app/modal.js b/javascripts/core/app/modal.js index 6a1aa0820..b445b9ab0 100644 --- a/javascripts/core/app/modal.js +++ b/javascripts/core/app/modal.js @@ -1,80 +1,111 @@ -import MessageModal from "@/components/modals/MessageModal"; -import CelestialQuoteModal from "@/components/modals/CelestialQuoteModal"; -import CloudSaveConflictModal from "@/components/modals/cloud/CloudSaveConflictModal"; import CloudLoadConflictModal from "@/components/modals/cloud/CloudLoadConflictModal"; import CloudManualLoginModal from "@/components/modals/cloud/CloudManualLoginModal"; +import CloudSaveConflictModal from "@/components/modals/cloud/CloudSaveConflictModal"; import EternityChallengeStartModal from "@/components/modals/challenges/EternityChallengeStartModal"; import InfinityChallengeStartModal from "@/components/modals/challenges/InfinityChallengeStartModal"; +import MessageModal from "@/components/modals/MessageModal"; import NormalChallengeStartModal from "@/components/modals/challenges/NormalChallengeStartModal"; -import DimensionBoostModal from "@/components/modals/prestige/DimensionBoostModal"; import AntimatterGalaxyModal from "@/components/modals/prestige/AntimatterGalaxyModal"; -import BigCrunchModal from "@/components/modals/prestige/BigCrunchModal"; -import ReplicantiGalaxyModal from "@/components/modals/prestige/ReplicantiGalaxyModal"; -import EternityModal from "@/components/modals/prestige/EternityModal"; -import EnterDilationModal from "@/components/modals/prestige/EnterDilationModal"; -import RealityModal from "@/components/modals/prestige/RealityModal"; -import ResetRealityModal from "@/components/modals/prestige/ResetRealityModal"; -import ExitCelestialModal from "@/components/modals/prestige/ExitCelestialModal"; -import EnterCelestialsModal from "@/components/modals/prestige/EnterCelestialsModal"; -import HardResetModal from "@/components/modals/prestige/HardResetModal"; -import SpeedrunModeModal from "@/components/modals/SpeedrunModeModal"; -import ChangeNameModal from "@/components/modals/ChangeNameModal"; import ArmageddonModal from "@/components/modals/prestige/ArmageddonModal"; +import BigCrunchModal from "@/components/modals/prestige/BigCrunchModal"; +import ChangeNameModal from "@/components/modals/ChangeNameModal"; +import DimensionBoostModal from "@/components/modals/prestige/DimensionBoostModal"; +import EnterCelestialsModal from "@/components/modals/prestige/EnterCelestialsModal"; +import EnterDilationModal from "@/components/modals/prestige/EnterDilationModal"; +import EternityModal from "@/components/modals/prestige/EternityModal"; +import ExitCelestialModal from "@/components/modals/prestige/ExitCelestialModal"; +import ExitDilationModal from "@/components/modals/prestige/ExitDilationModal"; +import HardResetModal from "@/components/modals/prestige/HardResetModal"; +import RealityModal from "@/components/modals/prestige/RealityModal"; +import ReplicantiGalaxyModal from "@/components/modals/prestige/ReplicantiGalaxyModal"; +import ResetRealityModal from "@/components/modals/prestige/ResetRealityModal"; +import SpeedrunModeModal from "@/components/modals/SpeedrunModeModal"; -import ConfirmationOptionsModal from "@/components/modals/options/ConfirmationOptionsModal"; -import InfoDisplayOptionsModal from "@/components/modals/options/InfoDisplayOptionsModal"; -import AwayProgressOptionsModal from "@/components/modals/options/AwayProgressOptionsModal"; -import HotkeysModal from "@/components/modals/options/HotkeysModal"; -import NewsOptionsModal from "@/components/modals/options/NewsOptionsModal"; import AnimationOptionsModal from "@/components/modals/options/AnimationOptionsModal"; -import PreferredTreeModal from "@/components/modals/options/PreferredTreeModal"; +import AwayProgressOptionsModal from "@/components/modals/options/AwayProgressOptionsModal"; +import ConfirmationOptionsModal from "@/components/modals/options/ConfirmationOptionsModal"; +import GlyphDisplayOptionsModal from "@/components/modals/options/GlyphDisplayOptionsModal"; import HiddenTabsModal from "@/components/modals/options/hidden-tabs/HiddenTabsModal"; +import HotkeysModal from "@/components/modals/options/HotkeysModal"; +import InfoDisplayOptionsModal from "@/components/modals/options/InfoDisplayOptionsModal"; +import NewsOptionsModal from "@/components/modals/options/NewsOptionsModal"; +import PreferredTreeModal from "@/components/modals/options/PreferredTreeModal"; import DeleteCompanionGlyphModal from "@/components/modals/glyph-management/DeleteCompanionGlyphModal"; import DeleteGlyphModal from "@/components/modals/glyph-management/DeleteGlyphModal"; -import PurgeGlyphModal from "@/components/modals/glyph-management/PurgeGlyphModal"; -import SacrificeGlyphModal from "@/components/modals/glyph-management/SacrificeGlyphModal"; -import RefineGlyphModal from "@/components/modals/glyph-management/RefineGlyphModal"; -import PurgeAllUnprotectedGlyphsModal from "@/components/modals/glyph-management/PurgeAllUnprotectedGlyphsModal"; import PurgeAllRejectedGlyphsModal from "@/components/modals/glyph-management/PurgeAllRejectedGlyphsModal"; +import PurgeAllUnprotectedGlyphsModal from "@/components/modals/glyph-management/PurgeAllUnprotectedGlyphsModal"; +import PurgeGlyphModal from "@/components/modals/glyph-management/PurgeGlyphModal"; +import RefineGlyphModal from "@/components/modals/glyph-management/RefineGlyphModal"; +import SacrificeGlyphModal from "@/components/modals/glyph-management/SacrificeGlyphModal"; -import H2PModal from "@/components/modals/H2PModal"; -import InformationModal from "@/components/modals/InformationModal"; -import GlyphShowcasePanelModal from "@/components/modals/GlyphShowcasePanelModal"; -import UndoGlyphModal from "@/components/modals/UndoGlyphModal"; -import ReplaceGlyphModal from "@/components/modals/ReplaceGlyphModal"; -import UiChoiceModal from "@/components/modals/UiChoiceModal"; -import AwayProgressModal from "@/components/modals/AwayProgressModal"; -import LoadGameModal from "@/components/modals/LoadGameModal"; -import ImportSaveModal from "@/components/modals/ImportSaveModal"; -import ImportAutomatorScriptModal from "@/components/modals/ImportAutomatorScriptModal"; -import DeleteAutomatorScriptModal from "@/components/modals/DeleteAutomatorScriptModal"; import AutomatorScriptTemplate from "@/components/modals/AutomatorScriptTemplate"; +import AwayProgressModal from "@/components/modals/AwayProgressModal"; +import BreakInfinityModal from "@/components/modals/BreakInfinityModal"; +import CatchupModal from "@/components/modals/catchup/CatchupModal"; +import ChangelogModal from "@/components/modals/ChangelogModal"; +import CreditsModal from "@/components/modals/CreditsModal"; +import DeleteAutomatorScriptModal from "@/components/modals/DeleteAutomatorScriptModal"; +import EnslavedHintsModal from "@/components/modals/EnslavedHintsModal"; +import GlyphSetSaveDeleteModal from "@/components/modals/GlyphSetSaveDeleteModal"; +import GlyphShowcasePanelModal from "@/components/modals/GlyphShowcasePanelModal"; +import H2PModal from "@/components/modals/H2PModal"; +import ImportAutomatorDataModal from "@/components/modals/ImportAutomatorDataModal"; +import ImportFileWarningModal from "@/components/modals/ImportFileWarningModal"; +import ImportSaveModal from "@/components/modals/ImportSaveModal"; +import InformationModal from "@/components/modals/InformationModal"; +import LoadGameModal from "@/components/modals/LoadGameModal"; +import PelleEffectsModal from "@/components/modals/PelleEffectsModal"; +import RealityGlyphCreationModal from "@/components/modals/RealityGlyphCreationModal"; +import ReplaceGlyphModal from "@/components/modals/ReplaceGlyphModal"; +import RespecIAPModal from "@/components/modals/RespecIAPModal"; +import SacrificeModal from "@/components/modals/SacrificeModal"; +import SingularityMilestonesModal from "@/components/modals/SingularityMilestonesModal"; import StdStoreModal from "@/components/modals/StdStoreModal"; import StudyStringModal from "@/components/modals/StudyStringModal"; -import SacrificeModal from "@/components/modals/SacrificeModal"; -import BreakInfinityModal from "@/components/modals/BreakInfinityModal"; -import GlyphSetSaveDeleteModal from "@/components/modals/GlyphSetSaveDeleteModal"; -import RealityGlyphCreationModal from "@/components/modals/RealityGlyphCreationModal"; -import EnslavedHintsModal from "@/components/modals/EnslavedHintsModal"; -import SingularityMilestonesModal from "@/components/modals/SingularityMilestonesModal"; -import PelleEffectsModal from "@/components/modals/PelleEffectsModal"; - +import SwitchAutomatorEditorModal from "@/components/modals/SwitchAutomatorEditorModal"; +import UiChoiceModal from "@/components/modals/UiChoiceModal"; +import UndoGlyphModal from "@/components/modals/UndoGlyphModal"; +let nextModalID = 0; export class Modal { - constructor(component, bare = false) { + constructor(component, priority = 0, closeEvent) { this._component = component; - this._bare = bare; this._modalConfig = {}; + this._priority = priority; + this._closeEvent = closeEvent; + } + + // We can't handle this in the Vue components because if the modal order changes, all the event listeners from the + // top modal end up getting removed from the EventHub due to the component being temporarily destroyed. This could + // result in the component sticking around because an event it was listening for happened while it wasn't on top. + applyCloseListeners(closeEvent) { + // Most of the time the close event will be a prestige event, in which case we want it to trigger on all higher + // prestiges as well + const prestigeOrder = [GAME_EVENT.DIMBOOST_AFTER, GAME_EVENT.GALAXY_RESET_AFTER, GAME_EVENT.BIG_CRUNCH_AFTER, + GAME_EVENT.ETERNITY_RESET_AFTER, GAME_EVENT.REALITY_RESET_AFTER]; + let shouldClose = false; + for (const prestige of prestigeOrder) { + if (prestige === closeEvent) shouldClose = true; + if (shouldClose) EventHub.ui.on(prestige, () => this.removeFromQueue(), this._component); + } + + // In a few cases we want to trigger a close based on a non-prestige event, so if the specified event wasn't in + // the prestige array above, we just add it on its own + if (!shouldClose) EventHub.ui.on(closeEvent, () => this.removeFromQueue(), this._component); } show(modalConfig) { if (!GameUI.initialized) return; + this._uniqueID = nextModalID++; this._props = Object.assign({}, modalConfig || {}); - if (ui.view.modal.queue.length === 0) ui.view.modal.current = this; - // New modals go to the back of the queue (shown last). - if (!ui.view.modal.queue.includes(this)) ui.view.modal.queue.push(this); + if (this._closeEvent) this.applyCloseListeners(this._closeEvent); + + const modalQueue = ui.view.modal.queue; + // Add this modal to the front of the queue and sort based on priority to ensure priority is maintained. + modalQueue.unshift(this); + Modal.sortModalQueue(); } get isOpen() { @@ -85,14 +116,30 @@ export class Modal { return this._component; } - get isBare() { - return this._bare; - } - get props() { return this._props; } + get priority() { + return this._priority; + } + + removeFromQueue() { + EventHub.ui.offAll(this._component); + ui.view.modal.queue = ui.view.modal.queue.filter(m => m._uniqueID !== this._uniqueID); + if (ui.view.modal.queue.length === 0) ui.view.modal.current = undefined; + else ui.view.modal.current = ui.view.modal.queue[0]; + } + + static sortModalQueue() { + const modalQueue = ui.view.modal.queue; + modalQueue.sort((x, y) => y.priority - x.priority); + // Filter out multiple instances of the same modal. + const singleQueue = [...new Set(modalQueue)]; + ui.view.modal.queue = singleQueue; + ui.view.modal.current = singleQueue[0]; + } + static hide() { if (!GameUI.initialized) return; ui.view.modal.queue.shift(); @@ -114,14 +161,19 @@ export class Modal { } static get isOpen() { - return ui.view.modal.current === this; + return ui.view.modal.current instanceof this; } } class ChallengeConfirmationModal extends Modal { show(id) { - this.id = id; - super.show(); + super.show({ id }); + } +} + +class TimeModal extends Modal { + show(diff) { + super.show({ diff }); } } @@ -132,108 +184,104 @@ Modal.startEternityChallenge = new ChallengeConfirmationModal(EternityChallengeS Modal.startInfinityChallenge = new ChallengeConfirmationModal(InfinityChallengeStartModal); Modal.startNormalChallenge = new ChallengeConfirmationModal(NormalChallengeStartModal); -Modal.dimensionBoost = new Modal(DimensionBoostModal); -Modal.antimatterGalaxy = new Modal(AntimatterGalaxyModal); -Modal.bigCrunch = new Modal(BigCrunchModal); -Modal.replicantiGalaxy = new Modal(ReplicantiGalaxyModal); -Modal.eternity = new Modal(EternityModal); -Modal.enterDilation = new Modal(EnterDilationModal); -Modal.reality = new Modal(RealityModal); -Modal.resetReality = new Modal(ResetRealityModal); -Modal.exitCelestialReality = new Modal(ExitCelestialModal); -Modal.celestials = new Modal(EnterCelestialsModal); -Modal.hardReset = new Modal(HardResetModal); +Modal.catchup = new TimeModal(CatchupModal, -1); + +Modal.dimensionBoost = new Modal(DimensionBoostModal, 1, GAME_EVENT.DIMBOOST_AFTER); + +Modal.antimatterGalaxy = new Modal(AntimatterGalaxyModal, 1, GAME_EVENT.GALAXY_RESET_AFTER); +Modal.bigCrunch = new Modal(BigCrunchModal, 1, GAME_EVENT.BIG_CRUNCH_AFTER); +Modal.replicantiGalaxy = new Modal(ReplicantiGalaxyModal, 1, GAME_EVENT.ETERNITY_RESET_AFTER); +Modal.eternity = new Modal(EternityModal, 1, GAME_EVENT.ETERNITY_RESET_AFTER); +Modal.enterDilation = new Modal(EnterDilationModal, 1, GAME_EVENT.REALITY_RESET_AFTER); +Modal.exitDilation = new Modal(ExitDilationModal, 1, GAME_EVENT.REALITY_RESET_AFTER); +Modal.reality = new Modal(RealityModal, 1, GAME_EVENT.REALITY_RESET_AFTER); +Modal.resetReality = new Modal(ResetRealityModal, 1, GAME_EVENT.REALITY_RESET_AFTER); +Modal.exitCelestialReality = new Modal(ExitCelestialModal, 1, GAME_EVENT.REALITY_RESET_AFTER); +Modal.celestials = new Modal(EnterCelestialsModal, 1); +Modal.hardReset = new Modal(HardResetModal, 1); Modal.enterSpeedrun = new Modal(SpeedrunModeModal); Modal.changeName = new Modal(ChangeNameModal); -Modal.armageddon = new Modal(ArmageddonModal); +Modal.armageddon = new Modal(ArmageddonModal, 1); Modal.confirmationOptions = new Modal(ConfirmationOptionsModal); Modal.infoDisplayOptions = new Modal(InfoDisplayOptionsModal); Modal.awayProgressOptions = new Modal(AwayProgressOptionsModal); +Modal.glyphDisplayOptions = new Modal(GlyphDisplayOptionsModal); Modal.hotkeys = new Modal(HotkeysModal); Modal.newsOptions = new Modal(NewsOptionsModal); Modal.animationOptions = new Modal(AnimationOptionsModal); Modal.hiddenTabs = new Modal(HiddenTabsModal); Modal.preferredTree = new Modal(PreferredTreeModal); -Modal.deleteCompanion = new Modal(DeleteCompanionGlyphModal); -Modal.glyphDelete = new Modal(DeleteGlyphModal); -Modal.glyphPurge = new Modal(PurgeGlyphModal); -Modal.glyphSacrifice = new Modal(SacrificeGlyphModal); -Modal.glyphRefine = new Modal(RefineGlyphModal); -Modal.deleteAllUnprotectedGlyphs = new Modal(PurgeAllUnprotectedGlyphsModal); -Modal.deleteAllRejectedGlyphs = new Modal(PurgeAllRejectedGlyphsModal); +Modal.deleteCompanion = new Modal(DeleteCompanionGlyphModal, 1); +Modal.glyphDelete = new Modal(DeleteGlyphModal, 1, GAME_EVENT.GLYPHS_CHANGED); +Modal.glyphPurge = new Modal(PurgeGlyphModal, 1, GAME_EVENT.GLYPHS_CHANGED); +Modal.glyphSacrifice = new Modal(SacrificeGlyphModal, 1, GAME_EVENT.GLYPHS_CHANGED); +Modal.glyphRefine = new Modal(RefineGlyphModal, 1, GAME_EVENT.GLYPHS_CHANGED); +Modal.deleteAllUnprotectedGlyphs = new Modal(PurgeAllUnprotectedGlyphsModal, 1, GAME_EVENT.GLYPHS_CHANGED); +Modal.deleteAllRejectedGlyphs = new Modal(PurgeAllRejectedGlyphsModal, 1, GAME_EVENT.GLYPHS_CHANGED); Modal.glyphShowcasePanel = new Modal(GlyphShowcasePanelModal); -Modal.glyphUndo = new Modal(UndoGlyphModal); -Modal.glyphReplace = new Modal(ReplaceGlyphModal); +Modal.glyphUndo = new Modal(UndoGlyphModal, 1, GAME_EVENT.REALITY_RESET_AFTER); +Modal.glyphReplace = new Modal(ReplaceGlyphModal, 1, GAME_EVENT.REALITY_RESET_AFTER); Modal.enslavedHints = new Modal(EnslavedHintsModal); Modal.realityGlyph = new Modal(RealityGlyphCreationModal); Modal.glyphSetSaveDelete = new Modal(GlyphSetSaveDeleteModal); Modal.uiChoice = new Modal(UiChoiceModal); Modal.h2p = new Modal(H2PModal); Modal.information = new Modal(InformationModal); +Modal.credits = new Modal(CreditsModal, 1); +Modal.changelog = new Modal(ChangelogModal, 1); Modal.awayProgress = new Modal(AwayProgressModal); Modal.loadGame = new Modal(LoadGameModal); Modal.import = new Modal(ImportSaveModal); -Modal.importScript = new Modal(ImportAutomatorScriptModal); +Modal.importWarning = new Modal(ImportFileWarningModal); +Modal.importScriptData = new Modal(ImportAutomatorDataModal); Modal.automatorScriptDelete = new Modal(DeleteAutomatorScriptModal); Modal.automatorScriptTemplate = new Modal(AutomatorScriptTemplate); +Modal.switchAutomatorEditorMode = new Modal(SwitchAutomatorEditorModal); Modal.shop = new Modal(StdStoreModal); Modal.studyString = new Modal(StudyStringModal); Modal.singularityMilestones = new Modal(SingularityMilestonesModal); Modal.pelleEffects = new Modal(PelleEffectsModal); -Modal.sacrifice = new Modal(SacrificeModal); -Modal.breakInfinity = new Modal(BreakInfinityModal); -Modal.celestialQuote = new class extends Modal { - show(celestial, lines) { - if (!GameUI.initialized) return; - const newLines = lines.map(l => Modal.celestialQuote.getLineMapping(celestial, l)); - if (ui.view.modal.queue.includes(this)) { - // This shouldn't come up often, but in case we do have a pile of quotes - // being shown in a row: - this.lines[this.lines.length - 1].isEndQuote = true; - this.lines.push(...newLines); - return; - } - super.show(); - this.lines = newLines; - } +Modal.sacrifice = new Modal(SacrificeModal, 1, GAME_EVENT.DIMBOOST_AFTER); +Modal.breakInfinity = new Modal(BreakInfinityModal, 1, GAME_EVENT.ETERNITY_RESET_AFTER); +Modal.respecIAP = new Modal(RespecIAPModal); - getLineMapping(defaultCel, defaultLine) { - let overrideCelestial = ""; - let l = defaultLine; - if (typeof l === "string") { - if (l.includes(""); - return x.substring(start + 2, end); - } - return ""; - } - - removeOverrideCel(x) { - if (x.includes(""); - return x.substring(0, start) + x.substring(end + 2); - } - return x; - } -}(CelestialQuoteModal, true); + return resources; +} Modal.cloudSaveConflict = new Modal(CloudSaveConflictModal); Modal.cloudLoadConflict = new Modal(CloudLoadConflictModal); @@ -248,64 +296,38 @@ Modal.addCloudConflict = function(saveId, saveComparison, cloudSave, localSave, local: getSaveInfo(localSave), onAccept }; +}; - function getSaveInfo(save) { - const resources = { - realTimePlayed: 0, - totalAntimatter: new Decimal(0), - infinities: new Decimal(0), - eternities: new Decimal(0), - realities: 0, - infinityPoints: new Decimal(0), - eternityPoints: new Decimal(0), - realityMachines: new Decimal(0), - imaginaryMachines: 0, - dilatedTime: new Decimal(0), - bestLevel: 0, - }; - resources.realTimePlayed = save.records.realTimePlayed; - resources.totalAntimatter.copyFrom(new Decimal(save.records.totalAntimatter)); - resources.infinities.copyFrom(new Decimal(save.infinities)); - resources.eternities.copyFrom(new Decimal(save.eternities)); - resources.realities = save.realities; - resources.infinityPoints.copyFrom(new Decimal(save.infinityPoints)); - resources.eternityPoints.copyFrom(new Decimal(save.eternityPoints)); - resources.realityMachines.copyFrom(new Decimal(save.reality.realityMachines)); - resources.imaginaryMachines = save.reality.iMCap; - resources.dilatedTime.copyFrom(new Decimal(save.dilation.dilatedTime)); - resources.bestLevel = save.records.bestReality.glyphLevel; - - return resources; - } +Modal.addImportConflict = function(importingSave, currentSave) { + Modal.hide(); + ui.view.modal.cloudConflict = { + importingSave: getSaveInfo(importingSave), + currentSave: getSaveInfo(currentSave) + }; }; Modal.message = new class extends Modal { - show(text, callback, closeButton = false) { + show(text, props = {}, messagePriority = 0) { if (!GameUI.initialized) return; + // It might be zero, so explicitly check for undefined + if (this.currPriority === undefined) this.currPriority = messagePriority; + else if (messagePriority < this.currPriority) return; + super.show(); - if (this.message === undefined) { - this.message = text; - this.callback = callback; - this.closeButton = closeButton; - } - if (!this.queue) this.queue = []; - this.queue.push({ text, callback, closeButton }); - // Sometimes we have stacked messages that get lost, since we don't have stacking modal system. + this.message = text; + this.callback = props.callback; + this.closeButton = props.closeButton ?? false; + EventHub.ui.offAll(this._component); + if (props.closeEvent) this.applyCloseListeners(props.closeEvent); + // TODO: remove this console.log // eslint-disable-next-line no-console console.log(`Modal message: ${text}`); } hide() { - if (this.queue.length <= 1) { - Modal.hide(); - } - this.queue.shift(); - if (this.queue && this.queue.length === 0) this.message = undefined; - else { - this.message = this.queue[0].text; - this.callback = this.queue[0].callback; - this.closeButton = this.queue[0].closeButton; - } + EventHub.ui.offAll(this._component); + this.currPriority = undefined; + Modal.hide(); } -}(MessageModal); +}(MessageModal, 2); \ No newline at end of file diff --git a/javascripts/core/app/notify.js b/javascripts/core/app/notify.js index 466e2fefc..817f3d678 100644 --- a/javascripts/core/app/notify.js +++ b/javascripts/core/app/notify.js @@ -35,6 +35,7 @@ export const notify = (function() { success: (text, duration) => showNotification(text, "o-notification--success", duration), error: (text, duration) => showNotification(text, "o-notification--error", duration), info: (text, duration) => showNotification(text, "o-notification--info", duration), + infinity: (text, duration) => showNotification(text, "o-notification--infinity", duration), eternity: (text, duration) => showNotification(text, "o-notification--eternity", duration), reality: (text, duration) => showNotification(text, "o-notification--reality", duration), blackHole: (text, duration) => showNotification(text, "o-notification--black-hole", duration), diff --git a/javascripts/core/app/options.js b/javascripts/core/app/options.js index 4e695e3ba..085033b19 100644 --- a/javascripts/core/app/options.js +++ b/javascripts/core/app/options.js @@ -1,5 +1,7 @@ import { sha512_256 } from "js-sha512"; +import FullScreenAnimationHandler from "../full-screen-animation-handler"; + export class GameOptions { static toggleNews() { @@ -66,10 +68,9 @@ export function isSecretImport(data) { export function tryImportSecret(data) { const index = secretImportIndex(data); - if (index === 0 && document.body.style.animation === "") { - document.body.style.animation = "barrelRoll 5s 1"; + if (index === 0) { + FullScreenAnimationHandler.display("a-barrel-roll", 5); SecretAchievement(15).unlock(); - setTimeout(() => document.body.style.animation = "", 5000); return true; } if (index === 1) { diff --git a/javascripts/core/app/themes.js b/javascripts/core/app/themes.js index cecf2ee8c..5774cb1f0 100644 --- a/javascripts/core/app/themes.js +++ b/javascripts/core/app/themes.js @@ -70,7 +70,7 @@ Theme.secretThemeIndex = function(name) { "ef853879b60fa6755d9599fd756c94d112f987c0cd596abf48b08f33af5ff537", "078570d37e6ffbf06e079e07c3c7987814e03436d00a17230ef5f24b1cb93290", "a3d64c3d1e1749b60b2b3dba10ed5ae9425300e9600ca05bcbafe4df6c69941f", - "d910565e1664748188b313768c370649230ca348cb6330fe9df73bcfa68d974d", + "530fac71cc0b151b24d966493a6f4a0817921b37e4d3e593439e624c214ab2b2", "cb72e4a679254df5f99110dc7a93924628b916d2e069e3ad206db92068cb0883", "c8fac64da08d674123c32c936b14115ab384fe556fd24e431eb184a8dde21137", "da3b3c152083f0c70245f104f06331497b97b52ac80edec05e26a33ee704cae7", @@ -103,7 +103,7 @@ Theme.tryUnlock = function(name) { Theme.set(prefix); SecretAchievement(25).unlock(); if (!isAlreadyUnlocked) { - GameUI.notify.success(`You have unlocked the ${name.capitalize()} theme!`); + GameUI.notify.success(`You have unlocked the ${name.capitalize()} theme!`, 5000); } return true; }; diff --git a/javascripts/core/app/ui.init.js b/javascripts/core/app/ui.init.js index d2f240753..6ed72e385 100644 --- a/javascripts/core/app/ui.init.js +++ b/javascripts/core/app/ui.init.js @@ -1,4 +1,3 @@ -// eslint-disable-next-line prefer-const export const state = { view: { modal: { @@ -7,6 +6,11 @@ export const state = { cloudConflict: [], progressBar: undefined, }, + quotes: { + queue: [], + current: undefined, + history: undefined + }, tabs: { reality: { openGlyphWeights: false, diff --git a/javascripts/core/app/ui.js b/javascripts/core/app/ui.js index a84c9749a..2f365ffb9 100644 --- a/javascripts/core/app/ui.js +++ b/javascripts/core/app/ui.js @@ -1,8 +1,10 @@ -import { notify } from "./notify.js"; -import { state } from "./ui.init.js"; import VTooltip from "v-tooltip"; -import { useLongPress, useRepeatingClick } from "./longpress"; import VueGtag from "vue-gtag"; + +import { useLongPress, useRepeatingClick } from "./longpress"; +import { notify } from "./notify"; +import { state } from "./ui.init"; + import GameUIComponent from "@/components/GameUIComponent"; Vue.mixin({ @@ -95,7 +97,7 @@ const ReactivityComplainer = { throw new Error(`Boi you fukked up - ${path} became REACTIVE (oh shite)`); } for (const key in obj) { - if (!obj.hasOwnProperty(key)) continue; + if (!Object.prototype.hasOwnProperty.call(obj, key)) continue; const prop = obj[key]; if (typeof prop === "object") { this.checkReactivity(prop, `${path}.${key}`); @@ -113,13 +115,13 @@ export const GameUI = { touchDevice: ("ontouchstart" in window || window.navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0 || (window.DocumentTouch && document instanceof DocumentTouch)), - dispatch(event) { + dispatch(event, args) { const index = this.events.indexOf(event); if (index !== -1) { this.events.splice(index, 1); } if (event !== GAME_EVENT.UPDATE) { - this.events.push(event); + this.events.push([event, args]); } if (this.flushPromise) return; this.flushPromise = Promise.resolve() @@ -132,7 +134,7 @@ export const GameUI = { PerformanceStats.start("Vue Update"); } for (const event of this.events) { - EventHub.ui.dispatch(event); + EventHub.ui.dispatch(event[0], event[1]); } EventHub.ui.dispatch(GAME_EVENT.UPDATE); ReactivityComplainer.complain(); diff --git a/javascripts/core/autobuyers/annihilation-autobuyer.js b/javascripts/core/autobuyers/annihilation-autobuyer.js index 2dfa16fc7..91f507c07 100644 --- a/javascripts/core/autobuyers/annihilation-autobuyer.js +++ b/javascripts/core/autobuyers/annihilation-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, AutobuyerState } from "./autobuyer.js"; +import { Autobuyer, AutobuyerState } from "./autobuyer"; Autobuyer.annihilation = new class AnnihilationAutobuyerState extends AutobuyerState { get data() { @@ -10,7 +10,7 @@ Autobuyer.annihilation = new class AnnihilationAutobuyerState extends AutobuyerS } get isUnlocked() { - return Laitela.darkMatterMult > 1; + return Laitela.darkMatterMult > 1 && !Pelle.isDoomed; } get multiplier() { diff --git a/javascripts/core/autobuyers/antimatter-dimension-autobuyer.js b/javascripts/core/autobuyers/antimatter-dimension-autobuyer.js index 8ec91a596..809a4bb40 100644 --- a/javascripts/core/autobuyers/antimatter-dimension-autobuyer.js +++ b/javascripts/core/autobuyers/antimatter-dimension-autobuyer.js @@ -1,5 +1,6 @@ -import { Autobuyer, UpgradeableAutobuyerState } from "./autobuyer.js"; -import { DC } from "../constants.js"; +import { DC } from "../constants"; + +import { Autobuyer, UpgradeableAutobuyerState } from "./autobuyer"; class AntimatterDimensionAutobuyerState extends UpgradeableAutobuyerState { get tier() { @@ -7,7 +8,7 @@ class AntimatterDimensionAutobuyerState extends UpgradeableAutobuyerState { } get name() { - return AntimatterDimension(this.tier).displayName; + return AntimatterDimension(this.tier).shortDisplayName; } get fullName() { @@ -15,16 +16,16 @@ class AntimatterDimensionAutobuyerState extends UpgradeableAutobuyerState { } get data() { - return player.auto.antimatterDims[this.tier - 1]; + return player.auto.antimatterDims.all[this.tier - 1]; } get baseInterval() { - return Player.defaultStart.auto.antimatterDims[this.tier - 1].interval; + return Player.defaultStart.auto.antimatterDims.all[this.tier - 1].interval; } get isUnlocked() { if (Pelle.isDisabled(`antimatterDimAutobuyer${this.tier}`)) return false; - return NormalChallenge(this.tier).isCompleted; + return this.data.isBought || this.canBeUpgraded; } get isBought() { @@ -39,6 +40,10 @@ class AntimatterDimensionAutobuyerState extends UpgradeableAutobuyerState { return !Pelle.isDisabled(`antimatterDimAutobuyer${this.tier}`); } + get canBeUpgraded() { + return NormalChallenge(this.tier).isCompleted; + } + get disabledByContinuum() { return Laitela.continuumActive; } @@ -113,7 +118,7 @@ class AntimatterDimensionAutobuyerState extends UpgradeableAutobuyerState { } get resetTickOn() { - return Perk.antimatterNoReset.isBought ? PRESTIGE_EVENT.ANTIMATTER_GALAXY : PRESTIGE_EVENT.DIMENSION_BOOST; + return Perk.antimatterNoReset.canBeApplied ? PRESTIGE_EVENT.ANTIMATTER_GALAXY : PRESTIGE_EVENT.DIMENSION_BOOST; } reset() { @@ -126,14 +131,17 @@ class AntimatterDimensionAutobuyerState extends UpgradeableAutobuyerState { static get entryCount() { return 8; } static get autobuyerGroupName() { return "Antimatter Dimension"; } + static get isActive() { return player.auto.antimatterDims.isActive; } + static set isActive(value) { player.auto.antimatterDims.isActive = value; } + static createAccessor() { const accessor = super.createAccessor(); - /** @returns {boolean} */ - accessor.allBought = () => accessor.zeroIndexed.every(x => x.isBought); - /** @returns {boolean} */ - // We can get away with this since allUnlimitedBulk is the same for all AD autos - accessor.allUnlimitedBulk = () => accessor.zeroIndexed[0].hasUnlimitedBulk; - accessor.bulkCap = accessor.zeroIndexed[0].bulkCap; + Object.defineProperties(accessor, { + allBought: { get: () => accessor.zeroIndexed.every(x => x.isBought) }, + // We can get away with this since allUnlimitedBulk is the same for all AD autos + allUnlimitedBulk: { get: () => accessor.zeroIndexed[0].hasUnlimitedBulk }, + bulkCap: { get: () => accessor.zeroIndexed[0].bulkCap } + }); return accessor; } } diff --git a/javascripts/core/autobuyers/autobuyer.js b/javascripts/core/autobuyers/autobuyer.js index 3dee7075e..0e7bcdeb6 100644 --- a/javascripts/core/autobuyers/autobuyer.js +++ b/javascripts/core/autobuyers/autobuyer.js @@ -19,7 +19,8 @@ export class AutobuyerState { get id() { return this._id; } get canTick() { - return this.isActive && player.auto.autobuyersOn && (this.isUnlocked || this.isBought); + const isDisabled = !player.auto.autobuyersOn || !this.constructor.isActive; + return this.isActive && !isDisabled && (this.isUnlocked || this.isBought); } get isActive() { @@ -46,7 +47,6 @@ export class AutobuyerState { // eslint-disable-next-line no-empty-function reset() { } - /** @returns {number} */ static get entryCount() { return 1; } /** @@ -54,6 +54,9 @@ export class AutobuyerState { * @returns {string} */ static get autobuyerGroupName() { throw new NotImplementedError(); } + static get isActive() { return true; } + /** @abstract */ + static set isActive(value) { throw new NotImplementedError(); } static createAccessor() { const entryCount = this.entryCount; @@ -62,16 +65,20 @@ export class AutobuyerState { const oneIndexed = [null, ...zeroIndexed]; /** @param {number} id */ const accessor = id => oneIndexed[id]; - accessor.oneIndexed = oneIndexed; - accessor.zeroIndexed = zeroIndexed; - accessor.entryCount = entryCount; - accessor.groupName = this.autobuyerGroupName; - /** @returns {boolean} */ - accessor.anyUnlocked = () => zeroIndexed.some(x => x.isUnlocked); - /** @returns {boolean} */ - accessor.allUnlocked = () => zeroIndexed.every(x => x.isUnlocked); - /** @returns {boolean} */ - accessor.allActive = () => zeroIndexed.every(x => x.isActive); + Object.defineProperties(accessor, { + oneIndexed: { get: () => oneIndexed }, + zeroIndexed: { get: () => zeroIndexed }, + entryCount: { get: () => entryCount }, + anyUnlocked: { get: () => zeroIndexed.some(x => x.isUnlocked) }, + allUnlocked: { get: () => zeroIndexed.every(x => x.isUnlocked) }, + allActive: { get: () => zeroIndexed.every(x => x.isActive) }, + groupName: { get: () => this.autobuyerGroupName }, + isActive: { + get: () => this.isActive, + set: value => { this.isActive = value; }, + }, + }); + accessor.toggle = () => this.isActive = !this.isActive; return accessor; } } @@ -157,8 +164,12 @@ export class UpgradeableAutobuyerState extends IntervaledAutobuyerState { static createAccessor() { const accessor = super.createAccessor(); - /** @returns {boolean} */ - accessor.allMaxedInterval = () => accessor.zeroIndexed.every(x => x.hasMaxedInterval); + Object.defineProperty(accessor, "allMaxedInterval", { + get: () => accessor.zeroIndexed.every(x => x.hasMaxedInterval) + }); + Object.defineProperty(accessor, "hasInstant", { + get: () => accessor.zeroIndexed.some(x => x.interval < player.options.updateRate) + }); return accessor; } } diff --git a/javascripts/core/autobuyers/autobuyers.js b/javascripts/core/autobuyers/autobuyers.js index 359859a07..850688c67 100644 --- a/javascripts/core/autobuyers/autobuyers.js +++ b/javascripts/core/autobuyers/autobuyers.js @@ -1,4 +1,4 @@ -import { Autobuyer } from "./autobuyer.js"; +import { Autobuyer } from "./autobuyer"; export const Autobuyers = (function() { const antimatterDimensions = Autobuyer.antimatterDimension.zeroIndexed; @@ -101,6 +101,6 @@ EventHub.logic.on(GAME_EVENT.REALITY_RESET_AFTER, () => Autobuyers.reset()); EventHub.logic.on(GAME_EVENT.DIMBOOST_AFTER, () => Autobuyers.resetTick(PRESTIGE_EVENT.DIMENSION_BOOST)); EventHub.logic.on(GAME_EVENT.GALAXY_RESET_AFTER, () => Autobuyers.resetTick(PRESTIGE_EVENT.ANTIMATTER_GALAXY)); -EventHub.logic.on(GAME_EVENT.INFINITY_RESET_AFTER, () => Autobuyers.resetTick(PRESTIGE_EVENT.INFINITY)); +EventHub.logic.on(GAME_EVENT.BIG_CRUNCH_AFTER, () => Autobuyers.resetTick(PRESTIGE_EVENT.INFINITY)); EventHub.logic.on(GAME_EVENT.ETERNITY_RESET_AFTER, () => Autobuyers.resetTick(PRESTIGE_EVENT.ETERNITY)); EventHub.logic.on(GAME_EVENT.REALITY_RESET_AFTER, () => Autobuyers.resetTick(PRESTIGE_EVENT.REALITY)); diff --git a/javascripts/core/autobuyers/big-crunch-autobuyer.js b/javascripts/core/autobuyers/big-crunch-autobuyer.js index fc6ee2eab..c2c192acd 100644 --- a/javascripts/core/autobuyers/big-crunch-autobuyer.js +++ b/javascripts/core/autobuyers/big-crunch-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, UpgradeableAutobuyerState } from "./autobuyer.js"; +import { Autobuyer, UpgradeableAutobuyerState } from "./autobuyer"; Autobuyer.bigCrunch = new class BigCrunchAutobuyerState extends UpgradeableAutobuyerState { get data() { @@ -10,6 +10,12 @@ Autobuyer.bigCrunch = new class BigCrunchAutobuyerState extends UpgradeableAutob } get isUnlocked() { + return Pelle.isDoomed + ? PelleStrikes.infinity.hasStrike + : this.canBeUpgraded; + } + + get canBeUpgraded() { return NormalChallenge(12).isCompleted; } diff --git a/javascripts/core/autobuyers/black-hole-power-autobuyer.js b/javascripts/core/autobuyers/black-hole-power-autobuyer.js index ba981abc8..e8132f84c 100644 --- a/javascripts/core/autobuyers/black-hole-power-autobuyer.js +++ b/javascripts/core/autobuyers/black-hole-power-autobuyer.js @@ -1,8 +1,8 @@ -import { Autobuyer, AutobuyerState } from "./autobuyer.js"; +import { Autobuyer, AutobuyerState } from "./autobuyer"; class BlackHolePowerAutobuyerState extends AutobuyerState { get data() { - return player.auto.blackHolePower[this.id - 1]; + return player.auto.blackHolePower.all[this.id - 1]; } get name() { @@ -10,15 +10,22 @@ class BlackHolePowerAutobuyerState extends AutobuyerState { } get isUnlocked() { - return Ra.has(RA_UNLOCKS.AUTO_BLACK_HOLE_POWER); + return Ra.unlocks.blackHolePowerAutobuyers.canBeApplied; + } + + get hasUnlimitedBulk() { + return true; } tick() { - BlackHole(this.id).powerUpgrade.purchase(); + const bh = BlackHole(this.id); + while (Currency.realityMachines.gte(bh.powerUpgrade.cost)) bh.powerUpgrade.purchase(); } static get entryCount() { return 2; } static get autobuyerGroupName() { return "Black Hole Power"; } + static get isActive() { return player.auto.blackHolePower.isActive; } + static set isActive(value) { player.auto.blackHolePower.isActive = value; } } Autobuyer.blackHolePower = BlackHolePowerAutobuyerState.createAccessor(); diff --git a/javascripts/core/autobuyers/dark-matter-dimension-ascension-autobuyer.js b/javascripts/core/autobuyers/dark-matter-dimension-ascension-autobuyer.js index d86f133f8..51925845a 100644 --- a/javascripts/core/autobuyers/dark-matter-dimension-ascension-autobuyer.js +++ b/javascripts/core/autobuyers/dark-matter-dimension-ascension-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, IntervaledAutobuyerState } from "./autobuyer.js"; +import { Autobuyer, IntervaledAutobuyerState } from "./autobuyer"; Autobuyer.darkMatterDimsAscension = new class DarkMatterDimensionAscensionAutobuyerState extends IntervaledAutobuyerState { @@ -11,7 +11,7 @@ new class DarkMatterDimensionAscensionAutobuyerState extends IntervaledAutobuyer } get isUnlocked() { - return SingularityMilestone.darkDimensionAutobuyers.isUnlocked; + return SingularityMilestone.darkDimensionAutobuyers.canBeApplied; } get interval() { diff --git a/javascripts/core/autobuyers/dark-matter-dimension-autobuyer.js b/javascripts/core/autobuyers/dark-matter-dimension-autobuyer.js index 502d53990..c81b53508 100644 --- a/javascripts/core/autobuyers/dark-matter-dimension-autobuyer.js +++ b/javascripts/core/autobuyers/dark-matter-dimension-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, IntervaledAutobuyerState } from "./autobuyer.js"; +import { Autobuyer, IntervaledAutobuyerState } from "./autobuyer"; Autobuyer.darkMatterDims = new class DarkMatterDimensionAutobuyerState extends IntervaledAutobuyerState { get data() { @@ -10,7 +10,7 @@ Autobuyer.darkMatterDims = new class DarkMatterDimensionAutobuyerState extends I } get isUnlocked() { - return SingularityMilestone.darkDimensionAutobuyers.isUnlocked; + return SingularityMilestone.darkDimensionAutobuyers.canBeApplied; } get interval() { diff --git a/javascripts/core/autobuyers/dilation-upgrade-autobuyer.js b/javascripts/core/autobuyers/dilation-upgrade-autobuyer.js index 0c46b71f7..882f5a263 100644 --- a/javascripts/core/autobuyers/dilation-upgrade-autobuyer.js +++ b/javascripts/core/autobuyers/dilation-upgrade-autobuyer.js @@ -1,10 +1,10 @@ -import { Autobuyer, IntervaledAutobuyerState } from "./autobuyer.js"; +import { Autobuyer, IntervaledAutobuyerState } from "./autobuyer"; class DilationUpgradeAutobuyerState extends IntervaledAutobuyerState { get _upgradeName() { return ["dtGain", "galaxyThreshold", "tachyonGain"][this.id - 1]; } get data() { - return player.auto.dilationUpgrades[this.id - 1]; + return player.auto.dilationUpgrades.all[this.id - 1]; } get name() { @@ -35,6 +35,8 @@ class DilationUpgradeAutobuyerState extends IntervaledAutobuyerState { static get entryCount() { return 3; } static get autobuyerGroupName() { return "Dilation Upgrade"; } + static get isActive() { return player.auto.dilationUpgrades.isActive; } + static set isActive(value) { player.auto.dilationUpgrades.isActive = value; } } Autobuyer.dilationUpgrade = DilationUpgradeAutobuyerState.createAccessor(); diff --git a/javascripts/core/autobuyers/dimboost-autobuyer.js b/javascripts/core/autobuyers/dimboost-autobuyer.js index 2b3de13a2..2bc3d4cc6 100644 --- a/javascripts/core/autobuyers/dimboost-autobuyer.js +++ b/javascripts/core/autobuyers/dimboost-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, UpgradeableAutobuyerState } from "./autobuyer.js"; +import { Autobuyer, UpgradeableAutobuyerState } from "./autobuyer"; Autobuyer.dimboost = new class DimBoostAutobuyerState extends UpgradeableAutobuyerState { get data() { @@ -11,6 +11,10 @@ Autobuyer.dimboost = new class DimBoostAutobuyerState extends UpgradeableAutobuy get isUnlocked() { if (Pelle.isDisabled("dimBoostAutobuyer")) return false; + return this.canBeUpgraded; + } + + get canBeUpgraded() { return NormalChallenge(10).isCompleted; } diff --git a/javascripts/core/autobuyers/eternity-autobuyer.js b/javascripts/core/autobuyers/eternity-autobuyer.js index cbf187cd4..2254ee2cd 100644 --- a/javascripts/core/autobuyers/eternity-autobuyer.js +++ b/javascripts/core/autobuyers/eternity-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, AutobuyerState } from "./autobuyer.js"; +import { Autobuyer, AutobuyerState } from "./autobuyer"; Autobuyer.eternity = new class EternityAutobuyerState extends AutobuyerState { get data() { diff --git a/javascripts/core/autobuyers/galaxy-autobuyer.js b/javascripts/core/autobuyers/galaxy-autobuyer.js index 0109fd878..3678813dc 100644 --- a/javascripts/core/autobuyers/galaxy-autobuyer.js +++ b/javascripts/core/autobuyers/galaxy-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, UpgradeableAutobuyerState } from "./autobuyer.js"; +import { Autobuyer, UpgradeableAutobuyerState } from "./autobuyer"; Autobuyer.galaxy = new class GalaxyAutobuyerState extends UpgradeableAutobuyerState { get data() { @@ -11,6 +11,10 @@ Autobuyer.galaxy = new class GalaxyAutobuyerState extends UpgradeableAutobuyerSt get isUnlocked() { if (Pelle.isDisabled("galaxyAutobuyer")) return false; + return this.canBeUpgraded; + } + + get canBeUpgraded() { return NormalChallenge(11).isCompleted; } diff --git a/javascripts/core/autobuyers/imaginary-upgrade-autobuyer.js b/javascripts/core/autobuyers/imaginary-upgrade-autobuyer.js index 18407020c..11501bdf9 100644 --- a/javascripts/core/autobuyers/imaginary-upgrade-autobuyer.js +++ b/javascripts/core/autobuyers/imaginary-upgrade-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, AutobuyerState } from "./autobuyer.js"; +import { Autobuyer, AutobuyerState } from "./autobuyer"; class ImaginaryUpgradeAutobuyerState extends AutobuyerState { get name() { @@ -6,19 +6,26 @@ class ImaginaryUpgradeAutobuyerState extends AutobuyerState { } get data() { - return player.auto.imaginaryUpgrades[this.id - 1]; + return player.auto.imaginaryUpgrades.all[this.id - 1]; } get isUnlocked() { - return ImaginaryUpgrade(20).isBought; + return ImaginaryUpgrade(20).canBeApplied; + } + + get hasUnlimitedBulk() { + return true; } tick() { - ImaginaryUpgrade(this.id).purchase(); + const upg = ImaginaryUpgrade(this.id); + while (Currency.imaginaryMachines.gte(upg.cost)) upg.purchase(); } static get entryCount() { return 10; } static get autobuyerGroupName() { return "Imaginary Upgrade"; } + static get isActive() { return player.auto.imaginaryUpgrades.isActive; } + static set isActive(value) { player.auto.imaginaryUpgrades.isActive = value; } } Autobuyer.imaginaryUpgrade = ImaginaryUpgradeAutobuyerState.createAccessor(); diff --git a/javascripts/core/autobuyers/index.js b/javascripts/core/autobuyers/index.js index c8b7d8a89..977d7d2f4 100644 --- a/javascripts/core/autobuyers/index.js +++ b/javascripts/core/autobuyers/index.js @@ -1,27 +1,27 @@ -import "./autobuyer.js"; +import "./autobuyer"; -import "./antimatter-dimension-autobuyer.js"; -import "./tickspeed-autobuyer.js"; -import "./dimboost-autobuyer.js"; -import "./galaxy-autobuyer.js"; -import "./big-crunch-autobuyer.js"; -import "./sacrifice-autobuyer.js"; -import "./eternity-autobuyer.js"; -import "./reality-autobuyer.js"; +import "./antimatter-dimension-autobuyer"; +import "./tickspeed-autobuyer"; +import "./dimboost-autobuyer"; +import "./galaxy-autobuyer"; +import "./big-crunch-autobuyer"; +import "./sacrifice-autobuyer"; +import "./eternity-autobuyer"; +import "./reality-autobuyer"; -import "./infinity-dimension-autobuyer.js"; -import "./time-dimension-autobuyer.js"; -import "./time-theorem-autobuyer.js"; -import "./black-hole-power-autobuyer.js"; -import "./reality-upgrade-autobuyer.js"; -import "./imaginary-upgrade-autobuyer.js"; -import "./replicanti-upgrade-autobuyer.js"; -import "./dilation-upgrade-autobuyer.js"; -import "./prestige-currency-multiplier-autobuyer.js"; -import "./replicanti-galaxy-autobuyer.js"; -import "./dark-matter-dimension-autobuyer.js"; -import "./dark-matter-dimension-ascension-autobuyer.js"; -import "./singularity-autobuyer.js"; -import "./annihilation-autobuyer.js"; +import "./infinity-dimension-autobuyer"; +import "./time-dimension-autobuyer"; +import "./time-theorem-autobuyer"; +import "./black-hole-power-autobuyer"; +import "./reality-upgrade-autobuyer"; +import "./imaginary-upgrade-autobuyer"; +import "./replicanti-upgrade-autobuyer"; +import "./dilation-upgrade-autobuyer"; +import "./prestige-currency-multiplier-autobuyer"; +import "./replicanti-galaxy-autobuyer"; +import "./dark-matter-dimension-autobuyer"; +import "./dark-matter-dimension-ascension-autobuyer"; +import "./singularity-autobuyer"; +import "./annihilation-autobuyer"; -export * from "./autobuyers.js"; +export * from "./autobuyers"; diff --git a/javascripts/core/autobuyers/infinity-dimension-autobuyer.js b/javascripts/core/autobuyers/infinity-dimension-autobuyer.js index f2c4400c7..2e064b289 100644 --- a/javascripts/core/autobuyers/infinity-dimension-autobuyer.js +++ b/javascripts/core/autobuyers/infinity-dimension-autobuyer.js @@ -1,5 +1,6 @@ -import { InfinityDimensions } from "../globals.js"; -import { Autobuyer, IntervaledAutobuyerState } from "./autobuyer.js"; +import { InfinityDimensions } from "../globals"; + +import { Autobuyer, IntervaledAutobuyerState } from "./autobuyer"; class InfinityDimensionAutobuyerState extends IntervaledAutobuyerState { get tier() { @@ -11,7 +12,7 @@ class InfinityDimensionAutobuyerState extends IntervaledAutobuyerState { } get name() { - return this.dimension.displayName; + return this.dimension.shortDisplayName; } get fullName() { @@ -19,7 +20,7 @@ class InfinityDimensionAutobuyerState extends IntervaledAutobuyerState { } get data() { - return player.auto.infinityDims[this.tier - 1]; + return player.auto.infinityDims.all[this.tier - 1]; } get interval() { @@ -27,7 +28,7 @@ class InfinityDimensionAutobuyerState extends IntervaledAutobuyerState { } get isUnlocked() { - return EternityMilestone.autobuyerID(this.tier).isReached || PelleUpgrade.IDAutobuyers.canBeApplied; + return EternityMilestone[`autobuyerID${this.tier}`].isReached || PelleUpgrade.IDAutobuyers.canBeApplied; } get resetTickOn() { @@ -49,6 +50,8 @@ class InfinityDimensionAutobuyerState extends IntervaledAutobuyerState { static get entryCount() { return 8; } static get autobuyerGroupName() { return "Infinity Dimension"; } + static get isActive() { return player.auto.infinityDims.isActive; } + static set isActive(value) { player.auto.infinityDims.isActive = value; } } Autobuyer.infinityDimension = InfinityDimensionAutobuyerState.createAccessor(); diff --git a/javascripts/core/autobuyers/prestige-currency-multiplier-autobuyer.js b/javascripts/core/autobuyers/prestige-currency-multiplier-autobuyer.js index 049e7a4c5..f85e85019 100644 --- a/javascripts/core/autobuyers/prestige-currency-multiplier-autobuyer.js +++ b/javascripts/core/autobuyers/prestige-currency-multiplier-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, AutobuyerState } from "./autobuyer.js"; +import { Autobuyer, AutobuyerState } from "./autobuyer"; Autobuyer.ipMult = new class IPMultAutobuyerState extends AutobuyerState { get data() { diff --git a/javascripts/core/autobuyers/reality-autobuyer.js b/javascripts/core/autobuyers/reality-autobuyer.js index ae43a486c..ed094e199 100644 --- a/javascripts/core/autobuyers/reality-autobuyer.js +++ b/javascripts/core/autobuyers/reality-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, AutobuyerState } from "./autobuyer.js"; +import { Autobuyer, AutobuyerState } from "./autobuyer"; Autobuyer.reality = new class RealityAutobuyerState extends AutobuyerState { get data() { diff --git a/javascripts/core/autobuyers/reality-upgrade-autobuyer.js b/javascripts/core/autobuyers/reality-upgrade-autobuyer.js index 985b5b56d..5286713d9 100644 --- a/javascripts/core/autobuyers/reality-upgrade-autobuyer.js +++ b/javascripts/core/autobuyers/reality-upgrade-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, AutobuyerState } from "./autobuyer.js"; +import { Autobuyer, AutobuyerState } from "./autobuyer"; class RealityUpgradeAutobuyerState extends AutobuyerState { get name() { @@ -6,19 +6,26 @@ class RealityUpgradeAutobuyerState extends AutobuyerState { } get data() { - return player.auto.realityUpgrades[this.id - 1]; + return player.auto.realityUpgrades.all[this.id - 1]; } get isUnlocked() { - return Ra.has(RA_UNLOCKS.AUTO_RU_AND_INSTANT_EC); + return Ra.unlocks.instantECAndRealityUpgradeAutobuyers.canBeApplied; + } + + get hasUnlimitedBulk() { + return true; } tick() { - RealityUpgrade(this.id).purchase(); + const upg = RealityUpgrade(this.id); + while (Currency.realityMachines.gte(upg.cost)) upg.purchase(); } static get entryCount() { return 5; } static get autobuyerGroupName() { return "Reality Upgrade"; } + static get isActive() { return player.auto.realityUpgrades.isActive; } + static set isActive(value) { player.auto.realityUpgrades.isActive = value; } } Autobuyer.realityUpgrade = RealityUpgradeAutobuyerState.createAccessor(); diff --git a/javascripts/core/autobuyers/replicanti-galaxy-autobuyer.js b/javascripts/core/autobuyers/replicanti-galaxy-autobuyer.js index 8f8b91032..08ce679b4 100644 --- a/javascripts/core/autobuyers/replicanti-galaxy-autobuyer.js +++ b/javascripts/core/autobuyers/replicanti-galaxy-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, AutobuyerState } from "./autobuyer.js"; +import { Autobuyer, AutobuyerState } from "./autobuyer"; Autobuyer.replicantiGalaxy = new class ReplicantiGalaxyAutobuyerState extends AutobuyerState { get data() { diff --git a/javascripts/core/autobuyers/replicanti-upgrade-autobuyer.js b/javascripts/core/autobuyers/replicanti-upgrade-autobuyer.js index 49fe1ade5..a56593770 100644 --- a/javascripts/core/autobuyers/replicanti-upgrade-autobuyer.js +++ b/javascripts/core/autobuyers/replicanti-upgrade-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, IntervaledAutobuyerState } from "./autobuyer.js"; +import { Autobuyer, IntervaledAutobuyerState } from "./autobuyer"; class ReplicantiUpgradeAutobuyerState extends IntervaledAutobuyerState { get _upgradeName() { return ["chance", "interval", "galaxies"][this.id - 1]; } @@ -8,7 +8,7 @@ class ReplicantiUpgradeAutobuyerState extends IntervaledAutobuyerState { } get data() { - return player.auto.replicantiUpgrades[this.id - 1]; + return player.auto.replicantiUpgrades.all[this.id - 1]; } get interval() { @@ -36,6 +36,8 @@ class ReplicantiUpgradeAutobuyerState extends IntervaledAutobuyerState { static get entryCount() { return 3; } static get autobuyerGroupName() { return "Replicanti Upgrade"; } + static get isActive() { return player.auto.replicantiUpgrades.isActive; } + static set isActive(value) { player.auto.replicantiUpgrades.isActive = value; } } Autobuyer.replicantiUpgrade = ReplicantiUpgradeAutobuyerState.createAccessor(); diff --git a/javascripts/core/autobuyers/sacrifice-autobuyer.js b/javascripts/core/autobuyers/sacrifice-autobuyer.js index 4f17908ac..49b475630 100644 --- a/javascripts/core/autobuyers/sacrifice-autobuyer.js +++ b/javascripts/core/autobuyers/sacrifice-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, AutobuyerState } from "./autobuyer.js"; +import { Autobuyer, AutobuyerState } from "./autobuyer"; Autobuyer.sacrifice = new class SacrificeAutobuyerState extends AutobuyerState { get data() { diff --git a/javascripts/core/autobuyers/singularity-autobuyer.js b/javascripts/core/autobuyers/singularity-autobuyer.js index cd791bd98..078fac0d4 100644 --- a/javascripts/core/autobuyers/singularity-autobuyer.js +++ b/javascripts/core/autobuyers/singularity-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, AutobuyerState } from "./autobuyer.js"; +import { Autobuyer, AutobuyerState } from "./autobuyer"; Autobuyer.singularity = new class SingularityAutobuyerState extends AutobuyerState { get data() { @@ -10,7 +10,7 @@ Autobuyer.singularity = new class SingularityAutobuyerState extends AutobuyerSta } get isUnlocked() { - return SingularityMilestone.autoCondense.isUnlocked; + return SingularityMilestone.autoCondense.canBeApplied; } get bulk() { diff --git a/javascripts/core/autobuyers/tickspeed-autobuyer.js b/javascripts/core/autobuyers/tickspeed-autobuyer.js index 6801f1532..885acf0e0 100644 --- a/javascripts/core/autobuyers/tickspeed-autobuyer.js +++ b/javascripts/core/autobuyers/tickspeed-autobuyer.js @@ -1,5 +1,6 @@ -import { Autobuyer, UpgradeableAutobuyerState } from "./autobuyer.js"; -import { DC } from "../constants.js"; +import { DC } from "../constants"; + +import { Autobuyer, UpgradeableAutobuyerState } from "./autobuyer"; Autobuyer.tickspeed = new class TickspeedAutobuyerState extends UpgradeableAutobuyerState { get data() { @@ -12,6 +13,10 @@ Autobuyer.tickspeed = new class TickspeedAutobuyerState extends UpgradeableAutob get isUnlocked() { if (Pelle.isDisabled("tickspeedAutobuyer")) return false; + return this.canBeUpgraded; + } + + get canBeUpgraded() { return NormalChallenge(9).isCompleted; } @@ -81,7 +86,7 @@ Autobuyer.tickspeed = new class TickspeedAutobuyerState extends UpgradeableAutob } get resetTickOn() { - return Perk.antimatterNoReset.isBought ? PRESTIGE_EVENT.ANTIMATTER_GALAXY : PRESTIGE_EVENT.DIMENSION_BOOST; + return Perk.antimatterNoReset.canBeApplied ? PRESTIGE_EVENT.ANTIMATTER_GALAXY : PRESTIGE_EVENT.DIMENSION_BOOST; } reset() { diff --git a/javascripts/core/autobuyers/time-dimension-autobuyer.js b/javascripts/core/autobuyers/time-dimension-autobuyer.js index ce9c29dad..c85ddae24 100644 --- a/javascripts/core/autobuyers/time-dimension-autobuyer.js +++ b/javascripts/core/autobuyers/time-dimension-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, IntervaledAutobuyerState } from "./autobuyer.js"; +import { Autobuyer, IntervaledAutobuyerState } from "./autobuyer"; class TimeDimensionAutobuyerState extends IntervaledAutobuyerState { get tier() { @@ -6,7 +6,7 @@ class TimeDimensionAutobuyerState extends IntervaledAutobuyerState { } get name() { - return TimeDimension(this.tier).displayName; + return TimeDimension(this.tier).shortDisplayName; } get fullName() { @@ -14,7 +14,7 @@ class TimeDimensionAutobuyerState extends IntervaledAutobuyerState { } get data() { - return player.auto.timeDims[this.tier - 1]; + return player.auto.timeDims.all[this.tier - 1]; } get interval() { @@ -46,6 +46,8 @@ class TimeDimensionAutobuyerState extends IntervaledAutobuyerState { static get entryCount() { return 8; } static get autobuyerGroupName() { return "Time Dimension"; } + static get isActive() { return player.auto.timeDims.isActive; } + static set isActive(value) { player.auto.timeDims.isActive = value; } } Autobuyer.timeDimension = TimeDimensionAutobuyerState.createAccessor(); diff --git a/javascripts/core/autobuyers/time-theorem-autobuyer.js b/javascripts/core/autobuyers/time-theorem-autobuyer.js index a63110afa..cc9e80fd5 100644 --- a/javascripts/core/autobuyers/time-theorem-autobuyer.js +++ b/javascripts/core/autobuyers/time-theorem-autobuyer.js @@ -1,4 +1,4 @@ -import { Autobuyer, AutobuyerState } from "./autobuyer.js"; +import { Autobuyer, AutobuyerState } from "./autobuyer"; Autobuyer.timeTheorem = new class TimeTheoremAutobuyerState extends AutobuyerState { get data() { @@ -14,7 +14,7 @@ Autobuyer.timeTheorem = new class TimeTheoremAutobuyerState extends AutobuyerSta } get hasUnlimitedBulk() { - return Perk.ttBuyMax.isBought; + return Perk.ttBuyMax.canBeApplied; } tick() { diff --git a/javascripts/core/automator/automator-backend.js b/javascripts/core/automator/automator-backend.js index 36f217764..f494dff69 100644 --- a/javascripts/core/automator/automator-backend.js +++ b/javascripts/core/automator/automator-backend.js @@ -1,3 +1,5 @@ +import { AutomatorPanels } from "../../../src/components/tabs/automator/AutomatorDocs"; + /** @abstract */ class AutomatorCommandInterface { constructor(id) { @@ -21,6 +23,7 @@ export const AUTOMATOR_COMMAND_STATUS = Object.freeze({ NEXT_TICK_NEXT_INSTRUCTION: 2, // This is used to handle some special cases, like branches/loops: SAME_INSTRUCTION: 3, + SKIP_INSTRUCTION: 4, }); export const AUTOMATOR_MODE = Object.freeze({ @@ -111,7 +114,6 @@ class AutomatorStackEntry { export class AutomatorScript { constructor(id) { - if (!id) throw new Error("Invalid Automator script ID"); this._id = id; this.compile(); } @@ -141,7 +143,7 @@ export class AutomatorScript { } save(content) { - this.persistent.content = content; + if (AutomatorData.isWithinLimit()) this.persistent.content = content; this.compile(); } @@ -149,14 +151,16 @@ export class AutomatorScript { this._compiled = AutomatorGrammar.compile(this.text).compiled; } - static create(name) { - let id = Object.keys(player.reality.automator.scripts).length + 1; + static create(name, content = "") { + const scripts = Object.keys(player.reality.automator.scripts); + const missingIndex = scripts.findIndex((x, y) => y + 1 !== Number(x)); + let id = 1 + (missingIndex === -1 ? scripts.length : missingIndex); // On a fresh save, this executes before player is properly initialized if (!player.reality.automator.scripts || id === 0) id = 1; player.reality.automator.scripts[id] = { id, name, - content: "", + content, }; return new AutomatorScript(id); } @@ -171,36 +175,54 @@ export const AutomatorData = { lastEvent: 0, eventLog: [], isEditorFullscreen: false, + needsRecompile: true, + cachedErrors: 0, + // This is to hold finished script templates as text in order to make the custom blocks for blockmato + blockTemplates: [], + + MAX_ALLOWED_SCRIPT_CHARACTERS: 10000, + MAX_ALLOWED_TOTAL_CHARACTERS: 60000, + MAX_ALLOWED_SCRIPT_NAME_LENGTH: 15, + MAX_ALLOWED_SCRIPT_COUNT: 20, + MAX_ALLOWED_CONSTANT_NAME_LENGTH: 20, + // Note that a study string with ALL studies in unshortened form without duplicated studies is ~230 characters + MAX_ALLOWED_CONSTANT_VALUE_LENGTH: 250, + MAX_ALLOWED_CONSTANT_COUNT: 30, + scriptIndex() { return player.reality.automator.state.editorScript; }, currentScriptName() { return player.reality.automator.scripts[this.scriptIndex()].name; }, - currentScriptText() { - return player.reality.automator.scripts[this.scriptIndex()].content; + currentScriptText(index) { + const toCheck = index || this.scriptIndex(); + return player.reality.automator.scripts[toCheck]?.content; }, - createNewScript(newScript, name) { - const newScriptID = Object.values(player.reality.automator.scripts).length + 1; - player.reality.automator.scripts[newScriptID] = { - id: `${newScriptID}`, - name, - content: newScript - }; + createNewScript(content, name) { + const newScript = AutomatorScript.create(name, content); GameUI.notify.info(`Imported Script "${name}"`); - player.reality.automator.state.editorScript = newScriptID; + player.reality.automator.state.editorScript = newScript.id; EventHub.dispatch(GAME_EVENT.AUTOMATOR_SAVE_CHANGED); }, - currentErrors(script) { - const toCheck = script || this.currentScriptText(); - return AutomatorGrammar.compile(toCheck).errors; + recalculateErrors() { + const toCheck = this.currentScriptText(); + this.cachedErrors = AutomatorGrammar.compile(toCheck).errors; + this.cachedErrors.sort((a, b) => a.startLine - b.startLine); + }, + currentErrors() { + if (this.needsRecompile) { + this.recalculateErrors(); + this.needsRecompile = false; + } + return this.cachedErrors; }, logCommandEvent(message, line) { const currTime = Date.now(); this.eventLog.push({ // Messages often overflow the 120 col limit and extra spacing gets included in the message - remove it message: message.replaceAll(/\s?\n\s+/gu, " "), - line, + line: AutomatorBackend.translateLineNumber(line), thisReality: Time.thisRealityRealTime.totalSeconds, timestamp: currTime, timegap: currTime - this.lastEvent @@ -212,11 +234,118 @@ export const AutomatorData = { clearEventLog() { this.eventLog = []; this.lastEvent = 0; + }, + // We need to get the current character count from the editor itself instead of the player object, because otherwise + // any changes made after getting above either limit will never be saved. Note that if the player is on the automator + // subtab before the automator is unlocked, editor is undefined + singleScriptCharacters() { + return player.reality.automator.type === AUTOMATOR_TYPE.TEXT + ? AutomatorTextUI.editor?.getDoc().getValue().length ?? 0 + : BlockAutomator.parseLines(BlockAutomator.lines).join("\n").length; + }, + totalScriptCharacters() { + return Object.values(player.reality.automator.scripts) + .filter(s => s.id !== this.scriptIndex()) + .map(s => s.content.length) + .reduce((sum, len) => sum + len, 0) + + this.singleScriptCharacters(); + }, + isWithinLimit() { + return this.singleScriptCharacters() <= this.MAX_ALLOWED_SCRIPT_CHARACTERS && + this.totalScriptCharacters() <= this.MAX_ALLOWED_TOTAL_CHARACTERS; + }, +}; + +export const LineEnum = { Active: "active", Event: "event", Error: "error" }; + +// Manages line highlighting in a way which is agnostic to the current editor mode (line or block). Ironically this is +// actually easier to manage in block mode as the Vue components render each line individually and we can just +// conditionally add classes in the template. The highlighting in text mode needs to be spliced and removed inline +// within the CodeMirror editor +export const AutomatorHighlighter = { + lines: { + active: -1, + event: -1, + error: -1, + }, + + updateHighlightedLine(line, key) { + if (player.reality.automator.type === AUTOMATOR_TYPE.TEXT && line !== -1) { + if (!AutomatorTextUI.editor) return; + this.removeHighlightedTextLine(key); + this.addHighlightedTextLine(line, key); + } else { + this.lines[key] = line; + } + }, + + // We need to specifically remove the highlighting class from the old line before splicing it in for the new line + removeHighlightedTextLine(key) { + const removedLine = this.lines[key] - 1; + AutomatorTextUI.editor.removeLineClass(removedLine, "background", `c-automator-editor__${key}-line`); + AutomatorTextUI.editor.removeLineClass(removedLine, "gutter", `c-automator-editor__${key}-line-gutter`); + this.lines[key] = -1; + }, + addHighlightedTextLine(line, key) { + AutomatorTextUI.editor.addLineClass(line - 1, "background", `c-automator-editor__${key}-line`); + AutomatorTextUI.editor.addLineClass(line - 1, "gutter", `c-automator-editor__${key}-line-gutter`); + this.lines[key] = line; + }, + + clearAllHighlightedLines() { + for (const lineType of Object.values(LineEnum)) { + if (player.reality.automator.type === AUTOMATOR_TYPE.TEXT && AutomatorTextUI.editor) { + for (let line = 0; line < AutomatorTextUI.editor.doc.size; line++) { + AutomatorTextUI.editor.removeLineClass(line, "background", `c-automator-editor__${lineType}-line`); + AutomatorTextUI.editor.removeLineClass(line, "gutter", `c-automator-editor__${lineType}-line-gutter`); + } + } + this.lines[lineType] = -1; + } + } +}; + +// Manages line highlighting in a way which is agnostic to the current editor mode (line or block) +export const AutomatorScroller = { + // Block editor counts lines differently due to modified loop structure; this method handles that internally + scrollToRawLine(line) { + const targetLine = player.reality.automator.type === AUTOMATOR_TYPE.TEXT + ? line + : AutomatorBackend.translateLineNumber(line); + this.scrollToLine(targetLine); + }, + + scrollToLine(line) { + let editor, textHeight, lineToScroll; + if (player.reality.automator.type === AUTOMATOR_TYPE.TEXT) { + // We can't use CodeMirror's scrollIntoView() method as that forces the entire viewport to keep the line in view. + // This can potentially cause a softlock with "follow execution" enabled on sufficiently short screens. + editor = document.querySelector(".CodeMirror-scroll"); + textHeight = AutomatorTextUI.editor.defaultTextHeight(); + lineToScroll = line + 1; + } else { + editor = BlockAutomator.editor; + textHeight = 34.5; + lineToScroll = line; + } + + // In both cases we might potentially try to scroll before the editor has properly initialized (ie. the automator + // itself ends up loading up faster than the editor UI element) + if (!editor) return; + + const paddedHeight = editor.clientHeight - 40; + const newScrollPos = textHeight * (lineToScroll - 1); + if (newScrollPos > editor.scrollTop + paddedHeight) editor.scrollTo(0, newScrollPos - paddedHeight); + if (newScrollPos < editor.scrollTop) editor.scrollTo(0, newScrollPos); + if (player.reality.automator.type === AUTOMATOR_TYPE.BLOCK) { + BlockAutomator.gutter.style.bottom = `${editor.scrollTop}px`; + } } }; export const AutomatorBackend = { MAX_COMMANDS_PER_UPDATE: 100, + hasJustCompleted: false, _scripts: [], get state() { @@ -243,20 +372,292 @@ export const AutomatorBackend = { return this.isOn && this.mode === AUTOMATOR_MODE.RUN; }, + findRawScriptObject(id) { + const scripts = player.reality.automator.scripts; + const index = Object.values(scripts).findIndex(s => s.id === id); + return scripts[parseInt(Object.keys(scripts)[index], 10)]; + }, + + get currentRunningScript() { + return this.findRawScriptObject(this.state.topLevelScript); + }, + + get currentEditingScript() { + return this.findRawScriptObject(player.reality.automator.state.editorScript); + }, + get scriptName() { - return this.findScript(this.state.topLevelScript).name; + return this.currentRunningScript?.name ?? ""; + }, + + hasDuplicateName(name) { + const nameArray = Object.values(player.reality.automator.scripts).map(s => s.name); + return nameArray.filter(n => n === name).length > 1; + }, + + // Scripts are internally stored and run as text, but block mode has a different layout for loops that + // shifts a lot of commands around. Therefore we need to conditionally change it based on mode in order + // to make sure the player is presented with the correct line number + translateLineNumber(num) { + if (player.reality.automator.type === AUTOMATOR_TYPE.TEXT) return num; + return BlockAutomator.lineNumber(num); }, get currentLineNumber() { - if (this.stack.top === null) - return -1; - return this.stack.top.lineNumber; + if (!this.stack.top) return -1; + return this.translateLineNumber(this.stack.top.lineNumber); }, get currentInterval() { return Math.clampMin(Math.pow(0.994, Currency.realities.value) * 500, 1); }, + get currentRawText() { + return this.currentRunningScript?.content ?? ""; + }, + + get currentScriptLength() { + return this.currentRawText.split("\n").length; + }, + + // Finds which study presets are referenced within the specified script + getUsedPresets(scriptID) { + const script = this.findRawScriptObject(scriptID); + if (!script) return null; + + const foundPresets = new Set(); + const lines = script.content.split("\n"); + for (const rawLine of lines) { + const matchPresetID = rawLine.match(/studies( nowait)? load id ([1-6])/ui); + if (matchPresetID) foundPresets.add(Number(matchPresetID[2]) - 1); + const matchPresetName = rawLine.match(/studies( nowait)? load name (\S+)/ui); + if (matchPresetName) { + // A script might pass the regex match, but actually be referencing a preset which doesn't exist by name + const presetID = player.timestudy.presets.findIndex(p => p.name === matchPresetName[2]); + if (presetID !== -1) foundPresets.add(presetID); + } + } + const presets = Array.from(foundPresets); + presets.sort(); + return presets; + }, + + // Finds which constants are referenced within the specified script + getUsedConstants(scriptID) { + const script = this.findRawScriptObject(scriptID); + if (!script) return null; + + const foundConstants = new Set(); + const lines = script.content.split("\n"); + for (const rawLine of lines) { + const availableConstants = Object.keys(player.reality.automator.constants); + // Needs a space-padded regex match so that (for example) a constant "unl" doesn't match to an unlock command + // Additionally we need a negative lookbehind in order to ignore matches with presets which have the same name + for (const key of availableConstants) { + if (rawLine.match(`(? "00004blob0000811,21,31" + // Note that the whole string can be unambiguously parsed from left-to-right regardless of the actual data contents. + // All numerical values are assumed to be exactly 5 characters long for consistency and since the script length limit + // is 5 digits long. + serializeAutomatorData(dataArray) { + const paddedNumber = num => `0000${num}`.slice(-5); + const segments = []; + for (const data of dataArray) { + segments.push(`${paddedNumber(data.length)}${data}`); + } + return segments.join(""); + }, + + // Inverse of the operation performed by serializeAutomatorData(). Can throw an error for malformed inputs, but this + // will always be caught farther up the call chain and interpreted properly as an invalid dataString. + deserializeAutomatorData(dataString) { + if (dataString === "") throw new Error("Attempted deserialization of empty string"); + const dataArray = []; + let remainingData = dataString; + while (remainingData.length > 0) { + const segmentLength = Number(remainingData.slice(0, 5)); + remainingData = remainingData.substr(5); + if (Number.isNaN(segmentLength) || remainingData.length < segmentLength) { + throw new Error("Inconsistent or malformed serialized automator data"); + } else { + const segmentData = remainingData.slice(0, segmentLength); + remainingData = remainingData.substr(segmentLength); + dataArray.push(segmentData); + } + } + return dataArray; + }, + + // This exports only the text contents of the currently-visible script + exportCurrentScriptContents() { + // Cut off leading and trailing whitespace + const trimmed = AutomatorData.currentScriptText().replace(/^\s*(.*?)\s*$/u, "$1"); + if (trimmed.length === 0) return null; + // Serialize the script name and content + const name = AutomatorData.currentScriptName(); + return GameSaveSerializer.encodeText(this.serializeAutomatorData([name, trimmed]), "automator script"); + }, + + // This parses script content from an encoded export string; does not actually import anything + parseScriptContents(rawInput) { + let decoded, parts; + try { + decoded = GameSaveSerializer.decodeText(rawInput, "automator script"); + parts = this.deserializeAutomatorData(decoded); + } catch (e) { + // TODO Remove everything but "return null" in this catch block before release; this is only here to maintain + // compatability with scripts from older test versions and will never be called on scripts exported post-release + if (decoded) { + parts = decoded.split("||"); + if (parts.length === 3 && parts[1].length === parseInt(parts[0], 10)) { + return { + name: parts[1], + content: parts[2], + }; + } + } + + return null; + } + + return { + name: parts[0], + content: parts[1], + }; + }, + + // Creates a new script from the supplied import string + importScriptContents(rawInput) { + const parsed = this.parseScriptContents(rawInput); + AutomatorData.createNewScript(parsed.content, parsed.name); + this.initializeFromSave(); + }, + + // This exports the selected script along with any constants and study presets it uses or references + exportFullScriptData(scriptID) { + const script = this.findRawScriptObject(scriptID); + const trimmed = script.content.replace(/^\s*(.*?)\s*$/u, "$1"); + if (trimmed.length === 0) return null; + + const foundPresets = new Set(); + const foundConstants = new Set(); + const lines = trimmed.split("\n"); + // We find just the keys first, the rest of the associated data is serialized later + for (const rawLine of lines) { + const matchPresetID = rawLine.match(/studies( nowait)? load id ([1-6])/ui); + if (matchPresetID) foundPresets.add(Number(matchPresetID[2]) - 1); + const matchPresetName = rawLine.match(/studies( nowait)? load name (\S+)/ui); + if (matchPresetName) { + // A script might pass the regex match, but actually be referencing a preset which doesn't exist by name + const presetID = player.timestudy.presets.findIndex(p => p.name === matchPresetName[2]); + if (presetID !== -1) foundPresets.add(presetID); + } + const availableConstants = Object.keys(player.reality.automator.constants); + for (const key of availableConstants) if (rawLine.match(`\\s${key}(\\s|$)`)) foundConstants.add(key); + } + + // Serialize presets + const presets = []; + for (const id of Array.from(foundPresets)) { + const preset = player.timestudy.presets[id]; + presets.push(`${id}:${preset?.name ?? ""}:${preset?.studies ?? ""}`); + } + + // Serialize constants + const constants = []; + for (const name of Array.from(foundConstants)) { + constants.push(`${name}:${player.reality.automator.constants[name]}`); + } + + // Serialize all the variables for the full data export + const serialized = this.serializeAutomatorData([script.name, presets.join("*"), constants.join("*"), trimmed]); + return GameSaveSerializer.encodeText(serialized, "automator data"); + }, + + // This parses scripts which also have attached information in the form of associated constants and study presets. + // Note that it doesn't actually import or assign the data to the save file at this point. + parseFullScriptData(rawInput) { + let decoded, parts; + try { + decoded = GameSaveSerializer.decodeText(rawInput, "automator data"); + parts = this.deserializeAutomatorData(decoded); + } catch (e) { + return null; + } + if (parts.length !== 4) return null; + + // Parse preset data (needs the conditional because otherwise it'll use the empty string to assign 0/undef/undef) + const presetData = parts[1]; + const presets = []; + if (presetData) { + for (const preset of presetData.split("*")) { + const props = preset.split(":"); + presets.push({ + id: Number(props[0]), + name: props[1], + studies: props[2], + }); + } + } + presets.sort((a, b) => a.id - b.id); + + // Parse constant data + const constantData = parts[2]; + const constants = []; + for (const constant of constantData.split("*")) { + if (constant === "") continue; + const props = constant.split(":"); + constants.push({ + key: props[0], + value: props[1], + }); + } + + return { + name: parts[0], + presets, + constants, + content: parts[3], + }; + }, + + // This imports a given script, with options supplied for ignoring included presets and constants + // within the import data. + importFullScriptData(rawInput, ignore) { + const parsed = this.parseFullScriptData(rawInput); + AutomatorData.createNewScript(parsed.content, parsed.name); + + if (!ignore.presets) { + for (const preset of parsed.presets) { + player.timestudy.presets[preset.id] = { name: preset.name, studies: preset.studies }; + } + } + + if (!ignore.constants) { + for (const constant of parsed.constants) { + const alreadyExists = player.reality.automator.constants[constant.key]; + const canMakeNew = Object.keys(player.reality.automator.constants).length < + AutomatorData.MAX_ALLOWED_CONSTANT_COUNT; + if (alreadyExists || canMakeNew) { + player.reality.automator.constants[constant.key] = constant.value; + } + } + } + + this.initializeFromSave(); + }, + update(diff) { if (!this.isOn) return; let stack; @@ -268,7 +669,7 @@ export const AutomatorBackend = { stack = AutomatorBackend.stack.top; // If single step completes the last line and repeat is off, the command stack will be empty and // scrolling will cause an error - if (stack) AutomatorTextUI.scrollToLine(stack.lineNumber - 1); + if (stack && this.state.followExecution) AutomatorScroller.scrollToRawLine(stack.lineNumber); this.state.mode = AUTOMATOR_MODE.PAUSE; return; case AUTOMATOR_MODE.RUN: @@ -291,18 +692,38 @@ export const AutomatorBackend = { step() { if (this.stack.isEmpty) return false; - switch (this.runCurrentCommand()) { - case AUTOMATOR_COMMAND_STATUS.SAME_INSTRUCTION: - return true; - case AUTOMATOR_COMMAND_STATUS.NEXT_INSTRUCTION: - return this.nextCommand(); - case AUTOMATOR_COMMAND_STATUS.NEXT_TICK_SAME_INSTRUCTION: - return false; - case AUTOMATOR_COMMAND_STATUS.NEXT_TICK_NEXT_INSTRUCTION: - this.nextCommand(); - return false; + for (let steps = 0; steps < 100 && !this.hasJustCompleted; steps++) { + switch (this.runCurrentCommand()) { + case AUTOMATOR_COMMAND_STATUS.SAME_INSTRUCTION: + return true; + case AUTOMATOR_COMMAND_STATUS.NEXT_INSTRUCTION: + return this.nextCommand(); + case AUTOMATOR_COMMAND_STATUS.NEXT_TICK_SAME_INSTRUCTION: + return false; + case AUTOMATOR_COMMAND_STATUS.NEXT_TICK_NEXT_INSTRUCTION: + this.nextCommand(); + return false; + case AUTOMATOR_COMMAND_STATUS.SKIP_INSTRUCTION: + this.nextCommand(); + } + + // We need to break out of the loop if the last commands are all SKIP_INSTRUCTION, or else it'll start + // trying to execute from an undefined stack if it isn't set to automatically repeat + if (!this.stack.top) this.hasJustCompleted = true; } - throw new Error("Unrecognized return code from command"); + + // This should in practice never happen by accident due to it requiring 100 consecutive commands that don't do + // anything (looping a smaller group of no-ops will instead trigger the loop check every tick). Nevertheless, + // better to not have an explicit infinite loop so that the game doesn't hang if the player decides to be funny + // and input 3000 comments in a row. If hasJustCompleted is true, then we actually broke out because the end of + // the script has no-ops and we just looped through them, and therefore shouldn't show these messages + if (!this.hasJustCompleted) { + GameUI.notify.error("Automator halted - too many consecutive no-ops detected"); + AutomatorData.logCommandEvent("Automator halted due to excessive no-op commands", this.currentLineNumber); + } + + this.stop(); + return false; }, singleStep() { @@ -351,27 +772,24 @@ export const AutomatorBackend = { }, findScript(id) { - // I tried really hard to convert IDs from strings into numbers for some cleanup but I just kept getting constant - // errors everywhere. It needs to be a number so that importing works properly without ID assignment being a mess, - // but apparently some deeper things seem to break in a way I can't easily fix. - return this._scripts.find(e => `${e.id}` === `${id}`); + return this._scripts.find(e => e.id === id); }, _createDefaultScript() { - const defaultScript = AutomatorScript.create("Untitled"); + const defaultScript = AutomatorScript.create("New Script"); this._scripts = [defaultScript]; this.state.topLevelScript = defaultScript.id; return defaultScript.id; }, initializeFromSave() { - const scriptIds = Object.keys(player.reality.automator.scripts); + const scriptIds = Object.keys(player.reality.automator.scripts).map(id => parseInt(id, 10)); if (scriptIds.length === 0) { scriptIds.push(this._createDefaultScript()); } else { this._scripts = scriptIds.map(s => new AutomatorScript(s)); } - if (!scriptIds.includes(`${this.state.topLevelScript}`)) this.state.topLevelScript = scriptIds[0]; + if (!scriptIds.includes(this.state.topLevelScript)) this.state.topLevelScript = scriptIds[0]; const currentScript = this.findScript(this.state.topLevelScript); if (currentScript.commands) { const commands = currentScript.commands; @@ -382,27 +800,34 @@ export const AutomatorBackend = { }, saveScript(id, data) { + if (!this.findScript(id)) return; this.findScript(id).save(data); if (id === this.state.topLevelScript) this.stop(); }, newScript() { - const newScript = AutomatorScript.create("Untitled"); + const newScript = AutomatorScript.create("New Script"); this._scripts.push(newScript); return newScript; }, + // Note that deleting scripts leaves gaps in the automator script indexing since automator scripts can't be + // dynamically re-indexed while the automator is running without causing a stutter from recompiling scripts. deleteScript(id) { + // We need to delete scripts from two places - in the savefile and compiled AutomatorScript Objects + const saveId = Object.values(player.reality.automator.scripts).findIndex(s => s.id === id); + delete player.reality.automator.scripts[parseInt(Object.keys(player.reality.automator.scripts)[saveId], 10)]; const idx = this._scripts.findIndex(e => e.id === id); this._scripts.splice(idx, 1); - delete player.reality.automator.scripts[id]; if (this._scripts.length === 0) { this._createDefaultScript(); + this.clearEditor(); } if (id === this.state.topLevelScript) { this.stop(); this.state.topLevelScript = this._scripts[0].id; } + EventHub.dispatch(GAME_EVENT.AUTOMATOR_SAVE_CHANGED); }, toggleRepeat() { @@ -422,7 +847,7 @@ export const AutomatorBackend = { const state = this.state; const focusedScript = state.topLevelScript === state.editorScript; if (focusedScript && this.isRunning && state.followExecution) { - AutomatorTextUI.scrollToLine(AutomatorBackend.stack.top.lineNumber - 1); + AutomatorScroller.scrollToRawLine(AutomatorBackend.stack.top.lineNumber); } }, @@ -434,6 +859,8 @@ export const AutomatorBackend = { stop() { this.stack.clear(); this.state.mode = AUTOMATOR_MODE.PAUSE; + this.hasJustCompleted = true; + AutomatorHighlighter.clearAllHighlightedLines(); }, pause() { @@ -441,6 +868,7 @@ export const AutomatorBackend = { }, start(scriptID = this.state.topLevelScript, initialMode = AUTOMATOR_MODE.RUN, compile = true) { + this.hasJustCompleted = false; this.state.topLevelScript = scriptID; const scriptObject = this.findScript(scriptID); if (compile) scriptObject.compile(); @@ -460,6 +888,34 @@ export const AutomatorBackend = { this.reset(this.stack._data[0].commands); }, + changeModes(scriptID) { + Tutorial.moveOn(TUTORIAL_STATE.AUTOMATOR); + if (player.reality.automator.type === AUTOMATOR_TYPE.BLOCK) { + // This saves the script after converting it. + BlockAutomator.parseTextFromBlocks(); + player.reality.automator.type = AUTOMATOR_TYPE.TEXT; + if (player.reality.automator.currentInfoPane === AutomatorPanels.BLOCKS) { + player.reality.automator.currentInfoPane = AutomatorPanels.COMMANDS; + } + } else { + const toConvert = AutomatorTextUI.editor.getDoc().getValue(); + // Needs to be called to update the lines prop in the BlockAutomator object + BlockAutomator.fromText(toConvert); + AutomatorBackend.saveScript(scriptID, toConvert); + player.reality.automator.type = AUTOMATOR_TYPE.BLOCK; + player.reality.automator.currentInfoPane = AutomatorPanels.BLOCKS; + } + AutomatorHighlighter.clearAllHighlightedLines(); + }, + + clearEditor() { + if (player.reality.automator.type === AUTOMATOR_TYPE.BLOCK) { + BlockAutomator.clearEditor(); + } else { + AutomatorTextUI.clearEditor(); + } + }, + stack: { _data: [], push(commands) { diff --git a/javascripts/core/automator/automator-codemirror.js b/javascripts/core/automator/automator-codemirror.js index e7f5e6ddb..e4c176487 100644 --- a/javascripts/core/automator/automator-codemirror.js +++ b/javascripts/core/automator/automator-codemirror.js @@ -1,12 +1,12 @@ -import { AutomatorGrammar } from "./parser.js"; -import { AutomatorLexer } from "./lexer.js"; +import { AutomatorGrammar } from "./parser"; +import { AutomatorLexer } from "./lexer"; (function() { function walkSuggestion(suggestion, prefix, output) { - if (suggestion.$autocomplete && - suggestion.$autocomplete.startsWith(prefix) && suggestion.$autocomplete !== prefix) { - output.add(suggestion.$autocomplete); - } + const hasAutocomplete = suggestion.$autocomplete && + suggestion.$autocomplete.startsWith(prefix) && suggestion.$autocomplete !== prefix; + const isUnlocked = suggestion.$unlocked ? suggestion.$unlocked() : true; + if (hasAutocomplete && isUnlocked) output.add(suggestion.$autocomplete); for (const s of suggestion.categoryMatches) { walkSuggestion(AutomatorLexer.tokenIds[s], prefix, output); } @@ -60,15 +60,10 @@ import { AutomatorLexer } from "./lexer.js"; { regex: /blob\s\s/ui, token: "blob" }, { // eslint-disable-next-line max-len - regex: /auto\s|if\s|pause\s|studies\s|tt\s|time theorems\s|until\s|wait\s|while\s|black[ \t]+hole\s|stored?[ \t]time\s|notify/ui, + regex: /(auto|if|pause|studies|time[ \t]+theorems?|until|wait|while|black[ \t]+hole|stored?[ \t]+game[ \t]+time|notify)\s/ui, token: "keyword", next: "commandArgs" }, - { - regex: /define\s/ui, - token: "keyword", - next: "defineIdentifier" - }, { regex: /start\s|unlock\s/ui, token: "keyword", @@ -85,8 +80,8 @@ import { AutomatorLexer } from "./lexer.js"; { sol: true, next: "start" }, { regex: /load(\s+|$)/ui, token: "variable-2", next: "studiesLoad" }, { regex: /respec/ui, token: "variable-2", next: "commandDone" }, + { regex: /purchase/ui, token: "variable-2", next: "studiesList" }, { regex: /nowait(\s+|$)/ui, token: "property" }, - { regex: /(?=\S)/ui, next: "studiesList" }, ], studiesList: [ commentRule, @@ -101,9 +96,15 @@ import { AutomatorLexer } from "./lexer.js"; studiesLoad: [ commentRule, { sol: true, next: "start" }, - { regex: /preset(\s+|$)/ui, token: "variable-2", next: "studiesLoadPreset" }, + { regex: /id(\s+|$)/ui, token: "variable-2", next: "studiesLoadId" }, + { regex: /name(\s+|$)/ui, token: "variable-2", next: "studiesLoadPreset" }, { regex: /\S+/ui, token: "error" }, ], + studiesLoadId: [ + commentRule, + { sol: true, next: "start" }, + { regex: /\d/ui, token: "qualifier", next: "commandDone" }, + ], studiesLoadPreset: [ commentRule, { sol: true, next: "start" }, @@ -122,11 +123,6 @@ import { AutomatorLexer } from "./lexer.js"; { regex: /\}/ui, dedent: true }, { regex: /\S+/ui, token: "error" }, ], - defineIdentifier: [ - commentRule, - { sol: true, next: "start" }, - { regex: /[a-zA-Z_][a-zA-Z_0-9]*/u, token: "variable", next: "commandArgs" }, - ], startUnlock: [ commentRule, { sol: true, next: "start" }, @@ -144,7 +140,7 @@ import { AutomatorLexer } from "./lexer.js"; { regex: /nowait(\s|$)/ui, token: "property" }, { regex: /".*"/ui, token: "string", next: "commandDone" }, { regex: /(on|off|dilation|load|respec)(\s|$)/ui, token: "variable-2" }, - { regex: /(preset|eternity|reality|use)(\s|$)/ui, token: "variable-2" }, + { regex: /(eternity|reality|use)(\s|$)/ui, token: "variable-2" }, { regex: /(antimatter|infinity|time)(\s|$|(?=,))/ui, token: "variable-2" }, { regex: /(active|passive|idle)(\s|$|(?=,))/ui, token: "variable-2" }, { regex: /(light|dark)(\s|$|(?=,))/ui, token: "variable-2" }, @@ -160,6 +156,7 @@ import { AutomatorLexer } from "./lexer.js"; { regex: / sec(onds ?) ?| min(utes ?) ?| hours ?/ui, token: "variable-2" }, { regex: /([0-9]+:[0-5][0-9]:[0-5][0-9]|[0-5]?[0-9]:[0-5][0-9]|t[1-4])/ui, token: "number" }, { regex: /-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/ui, token: "number" }, + { regex: /[a-zA-Z_][a-zA-Z_0-9]*/u, token: "variable" }, { regex: /\{/ui, indent: true, next: "commandDone" }, // This seems necessary to have a closing curly brace de-indent automatically in some cases { regex: /\}/ui, dedent: true }, diff --git a/javascripts/core/automator/automator-commands.js b/javascripts/core/automator/automator-commands.js index c1c87eaf9..34951c8f5 100644 --- a/javascripts/core/automator/automator-commands.js +++ b/javascripts/core/automator/automator-commands.js @@ -1,4 +1,4 @@ -import { AutomatorLexer } from "./lexer.js"; +import { AutomatorLexer } from "./lexer"; /** * Note: the $ shorthand for the parser object is required by Chevrotain. Don't mess with it. @@ -6,10 +6,8 @@ import { AutomatorLexer } from "./lexer.js"; export const AutomatorCommands = ((() => { const T = AutomatorLexer.tokenMap; - // The splitter tries to get a number 1 through 6, or anything else. Note: eslint complains - // about lack of u flag here for some reason. - // eslint-disable-next-line require-unicode-regexp - const presetSplitter = new RegExp(/preset[ \t]+(?:([1-6]$)|(.+$))/ui); + const presetSplitter = /name[ \t]+(.+$)/ui; + const idSplitter = /id[ \t]+(\d)/ui; function prestigeNotify(flag) { if (!AutomatorBackend.isOn) return; @@ -28,13 +26,13 @@ export const AutomatorCommands = ((() => { return { run: () => { if (!evalComparison()) { - AutomatorData.logCommandEvent(`Checked ${parseConditionalIntoText(ctx)} (false), - exiting loop at line ${ctx.RCurly[0].startLine + 1} (end of loop)`, ctx.startLine); + AutomatorData.logCommandEvent(`Checked ${parseConditionalIntoText(ctx)} (false), exiting loop at + line ${AutomatorBackend.translateLineNumber(ctx.RCurly[0].startLine + 1)} (end of loop)`, ctx.startLine); return AUTOMATOR_COMMAND_STATUS.NEXT_TICK_NEXT_INSTRUCTION; } AutomatorBackend.push(commands); - AutomatorData.logCommandEvent(`Checked ${parseConditionalIntoText(ctx)} (true), - moving to line ${ctx.LCurly[0].startLine + 1} (start of loop)`, ctx.startLine); + AutomatorData.logCommandEvent(`Checked ${parseConditionalIntoText(ctx)} (true), moving to + line ${AutomatorBackend.translateLineNumber(ctx.LCurly[0].startLine + 1)} (start of loop)`, ctx.startLine); return AUTOMATOR_COMMAND_STATUS.SAME_INSTRUCTION; }, blockCommands: commands, @@ -44,11 +42,12 @@ export const AutomatorCommands = ((() => { // Extracts the conditional out of a command and returns it as text function parseConditionalIntoText(ctx) { const comp = ctx.comparison[0].children; - const getters = comp.compareValue.map(cv => ( - cv.children.AutomatorCurrency - ? () => cv.children.AutomatorCurrency[0].image - : () => format(cv.children.$value, 2, 2) - )); + const getters = comp.compareValue.map(cv => { + if (cv.children.AutomatorCurrency) return () => cv.children.AutomatorCurrency[0].image; + const val = cv.children.$value; + if (typeof val === "string") return () => val; + return () => format(val, 2, 2); + }); const compareFn = comp.ComparisonOperator[0].image; return `${getters[0]()} ${compareFn} ${getters[1]()}`; } @@ -91,12 +90,6 @@ export const AutomatorCommands = ((() => { // eslint-disable-next-line complexity validate: (ctx, V) => { ctx.startLine = ctx.Auto[0].startLine; - if (ctx.PrestigeEvent && ctx.PrestigeEvent[0].tokenType === T.Reality && (ctx.duration || ctx.xHighest)) { - V.addError((ctx.duration || ctx.xHighest)[0], - "Auto Reality cannot be set to a duration or x highest", - "Use RM for Auto Reality"); - return false; - } if (ctx.PrestigeEvent && ctx.currencyAmount) { const desired$ = ctx.PrestigeEvent[0].tokenType.$prestigeCurrency; const specified$ = ctx.currencyAmount[0].children.AutomatorCurrency[0].tokenType.name; @@ -107,41 +100,51 @@ export const AutomatorCommands = ((() => { } } - if (ctx.PrestigeEvent && ctx.PrestigeEvent[0].tokenType === T.Infinity && - (ctx.duration || ctx.xHighest) && !EternityMilestone.bigCrunchModes.isReached) { - V.addError((ctx.duration || ctx.xHighest)[0], - "Advanced Infinity autobuyer settings are not unlocked", - `Reach ${quantifyInt("Eternity", EternityMilestone.bigCrunchModes.config.eternities)} to use this command`); - return false; + if (!ctx.PrestigeEvent) return true; + const advSetting = ctx.duration || ctx.xHighest; + // Do not change to switch statement; T.XXX are Objects, not primitive values + if (ctx.PrestigeEvent[0].tokenType === T.Infinity) { + if (!Autobuyer.bigCrunch.isUnlocked) { + V.addError(ctx.PrestigeEvent, "Infinity autobuyer is not unlocked", + "Complete the Big Crunch Autobuyer challenge to use this command"); + return false; + } + if (advSetting && !EternityMilestone.bigCrunchModes.isReached) { + V.addError((ctx.duration || ctx.xHighest)[0], + "Advanced Infinity autobuyer settings are not unlocked", + `Reach ${quantifyInt("Eternity", EternityMilestone.bigCrunchModes.config.eternities)} + to use this command`); + return false; + } + } + if (ctx.PrestigeEvent[0].tokenType === T.Eternity) { + if (!EternityMilestone.autobuyerEternity.isReached) { + V.addError(ctx.PrestigeEvent, "Eternity autobuyer is not unlocked", + `Reach ${quantifyInt("Eternity", EternityMilestone.autobuyerEternity.config.eternities)} + to use this command`); + return false; + } + if (advSetting && !RealityUpgrade(13).isBought) { + V.addError((ctx.duration || ctx.xHighest)[0], + "Advanced Eternity autobuyer settings are not unlocked", + "Purchase the Reality Upgrade which unlocks advanced Eternity autobuyer settings"); + return false; + } + } + if (ctx.PrestigeEvent[0].tokenType === T.Reality) { + if (!RealityUpgrade(25).isBought) { + V.addError(ctx.PrestigeEvent, "Reality autobuyer is not unlocked", + "Purchase the Reality Upgrade which unlocks the Reality autobuyer"); + return false; + } + if (advSetting) { + V.addError((ctx.duration || ctx.xHighest)[0], + "Auto Reality cannot be set to a duration or x highest", + "Use RM for Auto Reality"); + return false; + } } - if (ctx.PrestigeEvent && ctx.PrestigeEvent[0].tokenType === T.Eternity && - (ctx.duration || ctx.xHighest) && !RealityUpgrade(13).isBought) { - V.addError((ctx.duration || ctx.xHighest)[0], - "Advanced Eternity autobuyer settings are not unlocked", - "Purchase the Reality Upgrade which unlocks advanced Eternity autobuyer settings"); - return false; - } - - if (ctx.PrestigeEvent && ctx.PrestigeEvent[0].tokenType === T.Eternity && - !EternityMilestone.autobuyerEternity.isReached) { - V.addError(ctx.PrestigeEvent, "Eternity autobuyer is not unlocked", - `Reach ${quantifyInt("Eternity", EternityMilestone.autobuyerEternity.config.eternities)} - to use this command`); - return false; - } - - if (ctx.PrestigeEvent && ctx.PrestigeEvent[0].tokenType === T.Infinity && !NormalChallenge(12).isCompleted) { - V.addError(ctx.PrestigeEvent, "Infinity autobuyer is not unlocked", - "Complete the Big Crunch Autobuyer challenge to use this command"); - return false; - } - - if (ctx.PrestigeEvent && ctx.PrestigeEvent[0].tokenType === T.Reality && !RealityUpgrade(25).isBought) { - V.addError(ctx.PrestigeEvent, "Reality autobuyer is not unlocked", - "Purchase the Reality Upgrade which unlocks the Reality autobuyer"); - return false; - } return true; }, compile: ctx => { @@ -202,8 +205,8 @@ export const AutomatorCommands = ((() => { else input = (on ? "ON" : "OFF"); return { - target: ctx.PrestigeEvent[0].tokenType.name.toUpperCase(), - inputValue: input, + singleSelectionInput: ctx.PrestigeEvent[0].tokenType.name.toUpperCase(), + singleTextInput: input, ...automatorBlocksMap.AUTO }; } @@ -220,8 +223,13 @@ export const AutomatorCommands = ((() => { validate: (ctx, V) => { ctx.startLine = ctx.BlackHole[0].startLine; if (!BlackHole(1).isUnlocked) { - V.addError(ctx.BlackHole[0], "Black Hole is not unlocked", - "Unlock the Black Hole in order to pause or unpause it"); + if (Enslaved.isRunning || Pelle.isDisabled("blackhole")) { + V.addError(ctx.BlackHole[0], "Black Hole is disabled in your current Reality", + "Return to normal Reality conditions to use this command again"); + } else { + V.addError(ctx.BlackHole[0], "Black Hole is not unlocked", + "Unlock the Black Hole in order to pause or unpause it"); + } return false; } return true; @@ -235,7 +243,7 @@ export const AutomatorCommands = ((() => { }; }, blockify: ctx => ({ - target: ctx.On ? "ON" : "OFF", + singleSelectionInput: ctx.On ? "ON" : "OFF", ...automatorBlocksMap["BLACK HOLE"] }) }, @@ -249,7 +257,7 @@ export const AutomatorCommands = ((() => { return true; }, // This is an easter egg, it shouldn't do anything - compile: () => () => AUTOMATOR_COMMAND_STATUS.NEXT_INSTRUCTION, + compile: () => () => AUTOMATOR_COMMAND_STATUS.SKIP_INSTRUCTION, blockify: () => ({ ...automatorBlocksMap.BLOB, }) @@ -264,58 +272,12 @@ export const AutomatorCommands = ((() => { return true; }, // Comments should be no-ops - compile: () => () => AUTOMATOR_COMMAND_STATUS.NEXT_INSTRUCTION, + compile: () => () => AUTOMATOR_COMMAND_STATUS.SKIP_INSTRUCTION, blockify: ctx => ({ ...automatorBlocksMap.COMMENT, - inputValue: ctx.Comment[0].image.replace(/(#|\/\/)\s?/u, ""), + singleTextInput: ctx.Comment[0].image.replace(/(#|\/\/)\s?/u, ""), }) }, - { - id: "define", - block: null, - rule: $ => () => { - $.CONSUME(T.Define); - $.CONSUME(T.Identifier); - $.CONSUME(T.EqualSign); - $.OR([ - { ALT: () => $.SUBRULE($.duration) }, - { ALT: () => $.SUBRULE($.studyList) }, - ]); - }, - validate: (ctx, V) => { - ctx.startLine = ctx.Define[0].startLine; - if (!ctx.Identifier || ctx.Identifier[0].isInsertedInRecovery || ctx.Identifier[0].image === "") { - V.addError(ctx.Define, "Missing variable name", - "Provide a variable name that isn't a command name between DEFINE and ="); - return false; - } - return true; - }, - // Since define creates constants, they are all resolved at compile. The actual define instruction - // doesn't have to do anything. - compile: () => () => AUTOMATOR_COMMAND_STATUS.NEXT_INSTRUCTION, - blockify: ctx => { - const studyListData = ctx.studyList[0].children.studyListEntry; - const studyList = []; - for (const entry of studyListData) { - if (entry.children.NumberLiteral) { - // Single study ID or numerical value - studyList.push(entry.children.NumberLiteral[0].image); - } else if (entry.children.StudyPath) { - // Study path (eg. "time") - studyList.push(entry.children.StudyPath[0].image); - } else { - // Study range (eg. "41-71") - const range = entry.children.studyRange[0].children; - studyList.push(`${range.firstStudy[0].image}-${range.lastStudy[0].image}`); - } - } - return { - ...automatorBlocksMap.DEFINE, - inputValue: `${ctx.Identifier[0].image} = ${studyList.join(",")}`, - }; - } - }, { id: "ifBlock", rule: $ => () => { @@ -344,7 +306,7 @@ export const AutomatorCommands = ((() => { }; if (!evalComparison()) { AutomatorData.logCommandEvent(`Checked ${parseConditionalIntoText(ctx)} (false), - skipping to line ${ctx.RCurly[0].startLine + 1}`, ctx.startLine); + skipping to line ${AutomatorBackend.translateLineNumber(ctx.RCurly[0].startLine + 1)}`, ctx.startLine); return AUTOMATOR_COMMAND_STATUS.NEXT_INSTRUCTION; } AutomatorBackend.push(commands); @@ -363,7 +325,8 @@ export const AutomatorCommands = ((() => { nest: commands, ...automatorBlocksMap.IF, ...comparison, - target: standardizeAutomatorCurrencyName(comparison.target) + genericInput1: standardizeAutomatorValues(comparison.genericInput1), + genericInput2: standardizeAutomatorValues(comparison.genericInput2) }; } }, @@ -387,7 +350,7 @@ export const AutomatorCommands = ((() => { }, blockify: ctx => ({ ...automatorBlocksMap.NOTIFY, - inputValue: ctx.StringLiteral[0].image, + singleTextInput: ctx.StringLiteral[0].image, }) }, { @@ -404,6 +367,12 @@ export const AutomatorCommands = ((() => { ctx.startLine = ctx.Pause[0].startLine; let duration; if (ctx.Identifier) { + if (!V.isValidVarFormat(ctx.Identifier[0], AUTOMATOR_VAR_TYPES.DURATION)) { + V.addError(ctx, `Constant ${ctx.Identifier[0].image} is not a valid time duration constant`, + `Ensure that ${ctx.Identifier[0].image} is a number of seconds less than + ${format(Number.MAX_VALUE / 1000)}`); + return false; + } const lookup = V.lookupVar(ctx.Identifier[0], AUTOMATOR_VAR_TYPES.DURATION); duration = lookup ? lookup.value : lookup; } else { @@ -415,8 +384,14 @@ export const AutomatorCommands = ((() => { compile: ctx => { const duration = ctx.$duration; return S => { - const dur = ctx.duration[0].children; - const timeString = `${dur.NumberLiteral[0].image} ${dur.TimeUnit[0].image.replace("\\s", "")}`; + let timeString; + if (ctx.duration) { + const c = ctx.duration[0].children; + timeString = `${c.NumberLiteral[0].image} ${c.TimeUnit[0].image}`; + } else { + // This is the case for a defined constant; its value was parsed out during validation + timeString = TimeSpan.fromMilliseconds(duration); + } if (S.commandState === null) { S.commandState = { timeMs: 0 }; AutomatorData.logCommandEvent(`Pause started (waiting ${timeString})`, ctx.startLine); @@ -432,10 +407,16 @@ export const AutomatorCommands = ((() => { }; }, blockify: ctx => { - const c = ctx.duration[0].children; + let blockArg; + if (ctx.duration) { + const c = ctx.duration[0].children; + blockArg = `${c.NumberLiteral[0].image} ${c.TimeUnit[0].image}`; + } else { + blockArg = `${ctx.Identifier[0].image}`; + } return { ...automatorBlocksMap.PAUSE, - inputValue: `${c.NumberLiteral[0].image} ${c.TimeUnit[0].image}` + singleTextInput: blockArg }; } }, @@ -477,15 +458,15 @@ export const AutomatorCommands = ((() => { const available = prestigeToken.$prestigeAvailable(); if (!available) { if (!nowait) return AUTOMATOR_COMMAND_STATUS.NEXT_TICK_SAME_INSTRUCTION; - AutomatorData.logCommandEvent(`Auto-${ctx.PrestigeEvent.image} attempted, but skipped due to NOWAIT`, + AutomatorData.logCommandEvent(`${ctx.PrestigeEvent.image} attempted, but skipped due to NOWAIT`, ctx.startLine); return AUTOMATOR_COMMAND_STATUS.NEXT_INSTRUCTION; } if (respec) prestigeToken.$respec(); prestigeToken.$prestige(); const prestigeName = ctx.PrestigeEvent[0].image.toUpperCase(); - AutomatorData.logCommandEvent(`Auto-${prestigeName} triggered - (${findLastPrestigeRecord(prestigeName)})`, ctx.startLine); + AutomatorData.logCommandEvent(`${prestigeName} triggered (${findLastPrestigeRecord(prestigeName)})`, + ctx.startLine); return AUTOMATOR_COMMAND_STATUS.NEXT_TICK_NEXT_INSTRUCTION; }; }, @@ -493,7 +474,7 @@ export const AutomatorCommands = ((() => { ...automatorBlocksMap[ ctx.PrestigeEvent[0].tokenType.name.toUpperCase() ], - wait: ctx.Nowait === undefined, + nowait: ctx.Nowait !== undefined, respec: ctx.Respec !== undefined }) }, @@ -519,7 +500,7 @@ export const AutomatorCommands = ((() => { } return AUTOMATOR_COMMAND_STATUS.NEXT_TICK_SAME_INSTRUCTION; }, - blockify: () => ({ target: "DILATION", ...automatorBlocksMap.START }) + blockify: () => ({ singleSelectionInput: "DILATION", ...automatorBlocksMap.START }) }, { id: "startEC", @@ -553,15 +534,15 @@ export const AutomatorCommands = ((() => { }; }, blockify: ctx => ({ - target: "EC", - inputValue: ctx.eternityChallenge[0].children.$ecNumber, + singleSelectionInput: "EC", + singleTextInput: ctx.eternityChallenge[0].children.$ecNumber, ...automatorBlocksMap.START }) }, { - id: "storeTime", + id: "storeGameTime", rule: $ => () => { - $.CONSUME(T.StoreTime); + $.CONSUME(T.StoreGameTime); $.OR([ { ALT: () => $.CONSUME(T.On) }, { ALT: () => $.CONSUME(T.Off) }, @@ -569,10 +550,10 @@ export const AutomatorCommands = ((() => { ]); }, validate: (ctx, V) => { - ctx.startLine = ctx.StoreTime[0].startLine; + ctx.startLine = ctx.StoreGameTime[0].startLine; if (!Enslaved.isUnlocked) { - V.addError(ctx.StoreTime[0], "You do not yet know how to store time", - "Unlock the ability to store time"); + V.addError(ctx.StoreGameTime[0], "You do not yet know how to store game time", + "Unlock the ability to store game time"); return false; } return true; @@ -581,9 +562,9 @@ export const AutomatorCommands = ((() => { if (ctx.Use) return () => { if (Enslaved.isUnlocked) { Enslaved.useStoredTime(false); - AutomatorData.logCommandEvent(`Stored time used`, ctx.startLine); + AutomatorData.logCommandEvent(`Stored game time used`, ctx.startLine); } else { - AutomatorData.logCommandEvent(`Attempted to use stored time, but failed (not unlocked yet)`, + AutomatorData.logCommandEvent(`Attempted to use stored game time, but failed (not unlocked yet)`, ctx.startLine); } return AUTOMATOR_COMMAND_STATUS.NEXT_INSTRUCTION; @@ -591,14 +572,14 @@ export const AutomatorCommands = ((() => { const on = Boolean(ctx.On); return () => { if (on !== player.celestials.enslaved.isStoring) Enslaved.toggleStoreBlackHole(); - AutomatorData.logCommandEvent(`Storing time toggled ${ctx.On ? "ON" : "OFF"}`, ctx.startLine); + AutomatorData.logCommandEvent(`Storing game time toggled ${ctx.On ? "ON" : "OFF"}`, ctx.startLine); return AUTOMATOR_COMMAND_STATUS.NEXT_INSTRUCTION; }; }, blockify: ctx => ({ // eslint-disable-next-line no-nested-ternary - target: ctx.Use ? "USE" : (ctx.On ? "ON" : "OFF"), - ...automatorBlocksMap["STORE TIME"] + singleSelectionInput: ctx.Use ? "USE" : (ctx.On ? "ON" : "OFF"), + ...automatorBlocksMap["STORE GAME TIME"] }) }, { @@ -606,6 +587,7 @@ export const AutomatorCommands = ((() => { rule: $ => () => { $.CONSUME(T.Studies); $.OPTION(() => $.CONSUME(T.Nowait)); + $.CONSUME(T.Purchase); $.OR([ { ALT: () => $.SUBRULE($.studyList) }, { ALT: () => $.CONSUME1(T.Identifier) }, @@ -614,13 +596,18 @@ export const AutomatorCommands = ((() => { validate: (ctx, V) => { ctx.startLine = ctx.Studies[0].startLine; if (ctx.Identifier) { + if (!V.isValidVarFormat(ctx.Identifier[0], AUTOMATOR_VAR_TYPES.STUDIES)) { + V.addError(ctx, `Constant ${ctx.Identifier[0].image} is not a valid Time Study constant`, + `Ensure that ${ctx.Identifier[0].image} is a properly-formatted Time Study string`); + return false; + } const varInfo = V.lookupVar(ctx.Identifier[0], AUTOMATOR_VAR_TYPES.STUDIES); - if (!varInfo) return; ctx.$studies = varInfo.value; ctx.$studies.image = ctx.Identifier[0].image; } else if (ctx.studyList) { ctx.$studies = V.visit(ctx.studyList); } + return true; }, compile: ctx => { const studies = ctx.$studies; @@ -665,9 +652,9 @@ export const AutomatorCommands = ((() => { }; }, blockify: ctx => ({ - inputValue: ctx.$studies.image, - wait: ctx.Nowait === undefined, - ...automatorBlocksMap.STUDIES + singleTextInput: ctx.$studies.image, + nowait: ctx.Nowait !== undefined, + ...automatorBlocksMap["STUDIES PURCHASE"] }) }, { @@ -676,38 +663,53 @@ export const AutomatorCommands = ((() => { $.CONSUME(T.Studies); $.OPTION(() => $.CONSUME(T.Nowait)); $.CONSUME(T.Load); - $.CONSUME(T.Preset); + $.OR([ + { ALT: () => $.CONSUME1(T.Id) }, + { ALT: () => $.CONSUME1(T.Name) }, + ]); }, validate: (ctx, V) => { ctx.startLine = ctx.Studies[0].startLine; - if (!ctx.Preset || ctx.Preset[0].isInsertedInRecovery || ctx.Preset[0].image === "") { - V.addError(ctx, "Missing preset and preset name", - `Provide the name of a saved study preset from the Time Studies page. Note this command will not work - with presets with purely numerical names.`); - return false; + + if (ctx.Id) { + const split = idSplitter.exec(ctx.Id[0].image); + + if (!split || ctx.Id[0].isInsertedInRecovery) { + V.addError(ctx, "Missing preset id", + "Provide the id of a saved study preset slot from the Time Studies page"); + return false; + } + + const id = parseInt(split[1], 10); + if (id < 1 || id > 6) { + V.addError(ctx.Id[0], `Could not find a preset with an id of ${id}`, + "Type in a valid id (1 - 6) for your study preset"); + return false; + } + ctx.$presetIndex = id; + return true; } - const split = presetSplitter.exec(ctx.Preset[0].image); - if (!split) { - V.addError(ctx.Preset[0], "Missing preset name or number", - "Provide the name or index (1-6) of a saved study preset from the Time Studies page"); - return false; - } - ctx.Preset[0].splitPresetResult = split; - let presetIndex; - if (split[2]) { - // We don't need to do any verification if it's a number; if it's a name, we - // check to make sure it exists: - presetIndex = player.timestudy.presets.findIndex(e => e.name === split[2]) + 1; + + if (ctx.Name) { + const split = presetSplitter.exec(ctx.Name[0].image); + + if (!split || ctx.Name[0].isInsertedInRecovery) { + V.addError(ctx, "Missing preset name", + "Provide the name of a saved study preset from the Time Studies page"); + return false; + } + + // If it's a name, we check to make sure it exists: + const presetIndex = player.timestudy.presets.findIndex(e => e.name === split[1]) + 1; if (presetIndex === 0) { - V.addError(ctx.Preset[0], `Could not find preset named ${split[2]} (Note: Names are case-sensitive)`, + V.addError(ctx.Name[0], `Could not find preset named ${split[1]} (Note: Names are case-sensitive)`, "Check to make sure you typed in the correct name for your study preset"); return false; } - } else { - presetIndex = parseInt(split[1], 10); + ctx.$presetIndex = presetIndex; + return true; } - ctx.$presetIndex = presetIndex; - return true; + return false; }, compile: ctx => { const presetIndex = ctx.$presetIndex; @@ -720,10 +722,13 @@ export const AutomatorCommands = ((() => { // if there are then we keep trying on this line until there aren't, unless we are given nowait const missingStudyCount = imported.purchasedStudies .filter(s => !GameCache.currentStudyTree.value.purchasedStudies.includes(s)).length; + + const presetRepresentation = ctx.Name ? ctx.Name[0].image : ctx.Id[0].image; + if (missingStudyCount === 0) { - AutomatorData.logCommandEvent(`Fully loaded study preset ${ctx.Preset[0].image}`, ctx.startLine); + AutomatorData.logCommandEvent(`Fully loaded study preset ${presetRepresentation}`, ctx.startLine); } else if (afterCount > beforeCount) { - AutomatorData.logCommandEvent(`Partially loaded study preset ${ctx.Preset[0].image} + AutomatorData.logCommandEvent(`Partially loaded study preset ${presetRepresentation} (missing ${quantifyInt("study", missingStudyCount)})`, ctx.startLine); } return ctx.Nowait !== undefined || missingStudyCount === 0 @@ -732,9 +737,10 @@ export const AutomatorCommands = ((() => { }; }, blockify: ctx => ({ - inputValue: ctx.$presetIndex, - wait: ctx.Nowait === undefined, - ...automatorBlocksMap.LOAD + singleSelectionInput: ctx.Name ? "NAME" : "ID", + singleTextInput: ctx.Name ? player.timestudy.presets[ctx.$presetIndex - 1].name : ctx.$presetIndex, + nowait: ctx.Nowait !== undefined, + ...automatorBlocksMap["STUDIES LOAD"] }) }, { @@ -752,37 +758,7 @@ export const AutomatorCommands = ((() => { AutomatorData.logCommandEvent(`Turned study respec ON`, ctx.startLine); return AUTOMATOR_COMMAND_STATUS.NEXT_INSTRUCTION; }, - blockify: () => automatorBlocksMap.RESPEC - }, - { - id: "tt", - rule: $ => () => { - $.OPTION(() => $.CONSUME(T.Buy)); - $.CONSUME(T.TT); - $.CONSUME(T.TTCurrency); - }, - validate: ctx => { - ctx.startLine = (ctx.Buy || ctx.TT)[0].startLine; - return true; - }, - compile: ctx => { - const buyFunction = ctx.TTCurrency[0].tokenType.$buyTT; - return () => { - const boughtTT = buyFunction(); - if (boughtTT) { - AutomatorData.logCommandEvent(`${formatInt(boughtTT)} TT purchased with ${ctx.TTCurrency[0].image}`, - ctx.startLine); - return AUTOMATOR_COMMAND_STATUS.NEXT_INSTRUCTION; - } - AutomatorData.logCommandEvent(`Attempted to purchase TT with ${ctx.TTCurrency[0].image} - but could not afford any`, ctx.startLine); - return AUTOMATOR_COMMAND_STATUS.NEXT_TICK_NEXT_INSTRUCTION; - }; - }, - blockify: ctx => ({ - target: ctx.TTCurrency[0].tokenType.name.toUpperCase(), - ...automatorBlocksMap.TT - }) + blockify: () => automatorBlocksMap["STUDIES RESPEC"] }, { id: "unlockDilation", @@ -816,8 +792,8 @@ export const AutomatorCommands = ((() => { }; }, blockify: ctx => ({ - target: "DILATION", - wait: ctx.Nowait === undefined, + singleSelectionInput: "DILATION", + nowait: ctx.Nowait !== undefined, ...automatorBlocksMap.UNLOCK }) }, @@ -853,9 +829,9 @@ export const AutomatorCommands = ((() => { }; }, blockify: ctx => ({ - target: "EC", - inputValue: ctx.eternityChallenge[0].children.$ecNumber, - wait: ctx.Nowait === undefined, + singleSelectionInput: "EC", + singleTextInput: ctx.eternityChallenge[0].children.$ecNumber, + nowait: ctx.Nowait !== undefined, ...automatorBlocksMap.UNLOCK }) }, @@ -908,8 +884,9 @@ export const AutomatorCommands = ((() => { return AUTOMATOR_COMMAND_STATUS.NEXT_INSTRUCTION; } AutomatorBackend.push(commands); - AutomatorData.logCommandEvent(`${prestigeName} prestige has not occurred yet, - moving to line ${ctx.LCurly[0].startLine + 1} (start of until loop)`, ctx.startLine); + AutomatorData.logCommandEvent(`${prestigeName} prestige has not occurred yet, moving to line + ${AutomatorBackend.translateLineNumber(ctx.LCurly[0].startLine + 1)} (start of until loop)`, + ctx.startLine); return AUTOMATOR_COMMAND_STATUS.SAME_INSTRUCTION; }, blockCommands: commands @@ -924,11 +901,12 @@ export const AutomatorCommands = ((() => { nest: commands, ...automatorBlocksMap.UNTIL, ...comparison, - target: standardizeAutomatorCurrencyName(comparison.target) + genericInput1: standardizeAutomatorValues(comparison.genericInput1), + genericInput2: standardizeAutomatorValues(comparison.genericInput2) }; } return { - target: ctx.PrestigeEvent[0].tokenType.name.toUpperCase(), + genericInput1: ctx.PrestigeEvent[0].tokenType.name.toUpperCase(), nest: commands, ...automatorBlocksMap.UNTIL }; @@ -974,7 +952,8 @@ export const AutomatorCommands = ((() => { nest: commands, ...automatorBlocksMap.WAIT, ...comparison, - target: standardizeAutomatorCurrencyName(comparison.target) + genericInput1: standardizeAutomatorValues(comparison.genericInput1), + genericInput2: standardizeAutomatorValues(comparison.genericInput2) }; } }, @@ -1012,7 +991,7 @@ export const AutomatorCommands = ((() => { }; }, blockify: ctx => ({ - target: ctx.PrestigeEvent[0].tokenType.name.toUpperCase(), + genericInput1: ctx.PrestigeEvent[0].tokenType.name.toUpperCase(), ...automatorBlocksMap.WAIT }) }, @@ -1039,7 +1018,8 @@ export const AutomatorCommands = ((() => { nest: commands, ...automatorBlocksMap.WHILE, ...comparison, - target: standardizeAutomatorCurrencyName(comparison.target) + genericInput1: standardizeAutomatorValues(comparison.genericInput1), + genericInput2: standardizeAutomatorValues(comparison.genericInput2) }; } } diff --git a/javascripts/core/automator/automator-points.js b/javascripts/core/automator/automator-points.js index 53993b9c5..0fafdacba 100644 --- a/javascripts/core/automator/automator-points.js +++ b/javascripts/core/automator/automator-points.js @@ -35,19 +35,17 @@ export const AutomatorPoints = { } }; -GameDatabase.reality.otherAutomatorPoints = (function() { - return [ - { - name: "Reality Count", - automatorPoints: () => 2 * Math.clampMax(Currency.realities.value, 100), - shortDescription: () => `+${formatInt(2)} per Reality, up to ${formatInt(100)} Realities`, - symbol: "Ϟ", - }, - { - name: "Black Hole", - automatorPoints: () => (BlackHole(1).isUnlocked ? 10 : 0), - shortDescription: () => `Unlocking gives ${formatInt(10)} AP`, - symbol: "", - }, - ]; -}()); +GameDatabase.reality.otherAutomatorPoints = [ + { + name: "Reality Count", + automatorPoints: () => 2 * Math.clampMax(Currency.realities.value, 50), + shortDescription: () => `+${formatInt(2)} per Reality, up to ${formatInt(50)} Realities`, + symbol: "Ϟ", + }, + { + name: "Black Hole", + automatorPoints: () => (BlackHole(1).isUnlocked ? 10 : 0), + shortDescription: () => `Unlocking gives ${formatInt(10)} AP`, + symbol: "", + }, +]; diff --git a/javascripts/core/automator/compiler.js b/javascripts/core/automator/compiler.js index 23cc57247..459f887df 100644 --- a/javascripts/core/automator/compiler.js +++ b/javascripts/core/automator/compiler.js @@ -1,6 +1,6 @@ -import { AutomatorCommands } from "./automator-commands.js"; -import { AutomatorGrammar } from "./parser.js"; -import { AutomatorLexer } from "./lexer.js"; +import { AutomatorCommands } from "./automator-commands"; +import { AutomatorGrammar } from "./parser"; +import { AutomatorLexer } from "./lexer"; (function() { if (AutomatorGrammar === undefined) { @@ -24,6 +24,16 @@ import { AutomatorLexer } from "./lexer.js"; if (ownMethod) ownMethod.call(this, ctx); }; } + + const lexResult = AutomatorLexer.lexer.tokenize(rawText); + const tokens = lexResult.tokens; + parser.input = tokens; + this.parseResult = parser.script(); + this.visit(this.parseResult); + this.addLexerErrors(lexResult.errors); + this.addParserErrors(parser.errors, tokens); + this.modifyErrorMessages(); + this.errorCount = lexResult.errors.length + this.errors.length + parser.errors.length; } addLexerErrors(errors) { @@ -154,6 +164,11 @@ import { AutomatorLexer } from "./lexer.js"; modifiedErrors.push(err); lastLine = err.startLine; } + + for (const err of modifiedErrors) { + err.startLine = AutomatorBackend.translateLineNumber(err.startLine); + } + this.errors = modifiedErrors; } @@ -175,34 +190,58 @@ import { AutomatorLexer } from "./lexer.js"; lookupVar(identifier, type) { const varName = identifier.image; - const varInfo = this.variables[varName]; - if (varInfo === undefined) { + const varInfo = {}; + const constants = player.reality.automator.constants; + if (!Object.keys(constants).includes(varName)) { this.addError(identifier, `Variable ${varName} has not been defined`, - `Use DEFINE to define ${varName} in order to reference it, or check for typos`); + `Use the definition panel to define ${varName} in order to reference it, or check for typos`); return undefined; } - if (varInfo.type === AUTOMATOR_VAR_TYPES.UNKNOWN) { - varInfo.firstUseLineNumber = identifier.image.startLine; - varInfo.type = type; - if (type === AUTOMATOR_VAR_TYPES.STUDIES) { - // The only time we have an unknown studies is if there was only one listed + const value = constants[varName]; + + let tree; + switch (type) { + case AUTOMATOR_VAR_TYPES.NUMBER: + varInfo.value = new Decimal(value); + break; + case AUTOMATOR_VAR_TYPES.STUDIES: + tree = new TimeStudyTree(value); varInfo.value = { - normal: [varInfo.value.toNumber()], - ec: 0 + normal: tree.selectedStudies.map(ts => ts.id), + ec: tree.ec }; - } - } else if (varInfo.type !== type) { - const inferenceMessage = varInfo.firstUseLineNumber - ? `\nIts use on line ${varInfo.firstUseLineNumber} identified it as a ${varInfo.type.name}` - : ""; - this.addError(identifier, `Variable ${varName} is not a ${type.name}${inferenceMessage}`, - "Defined variables cannot be used as both studies and numbers - define a second variable instead"); - return undefined; + break; + case AUTOMATOR_VAR_TYPES.DURATION: + varInfo.value = parseInt(1000 * value, 10); + break; + default: + throw new Error("Unrecognized variable format in automator constant lookup"); } - if (varInfo.value === undefined) throw new Error("Unexpected undefined Automator variable value"); + return varInfo; } + isValidVarFormat(identifier, type) { + const varName = identifier.image; + const constants = player.reality.automator.constants; + if (!Object.keys(constants).includes(varName)) return false; + const value = constants[varName]; + + switch (type) { + case AUTOMATOR_VAR_TYPES.NUMBER: + // We can't rely on native Decimal parsing here because it largely just discards input past invalid + // characters and constructs something based on the start of the input string. Notably, this makes + // things like new Decimal("11,21,31") return 11 instead of something indicating an error. + return value.match(/^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?$/u); + case AUTOMATOR_VAR_TYPES.STUDIES: + return new TimeStudyTree(value).purchasedStudies.length > 0; + case AUTOMATOR_VAR_TYPES.DURATION: + return !Number.isNaN(parseInt(1000 * value, 10)); + default: + throw new Error("Unrecognized variable format in automator constant lookup"); + } + } + duration(ctx) { if (ctx.$value) return ctx.$value; if (!ctx.TimeUnit || ctx.TimeUnit[0].isInsertedInRecovery) { @@ -238,50 +277,6 @@ import { AutomatorLexer } from "./lexer.js"; return ctx.$value; } - define(ctx) { - const varName = ctx.Identifier[0].image; - if (this.variables[varName] !== undefined) { - this.addError(ctx.Identifier[0], - `Variable ${varName} already defined on line ${this.variables[varName].definitionLineNumber}`, - "Variables cannot be defined twice; remove or rename the second DEFINE"); - return; - } - if (!ctx.duration && !ctx.studyList) return; - const def = { - name: varName, - definitionLineNumber: ctx.Identifier[0].startLine, - firstUseLineNumber: 0, - type: AUTOMATOR_VAR_TYPES.UNKNOWN, - value: undefined, - }; - this.variables[varName] = def; - if (ctx.duration) { - def.type = AUTOMATOR_VAR_TYPES.DURATION; - def.value = this.visit(ctx.duration); - return; - } - // We don't visit studyList because it might actually be just a number in this case - const studies = ctx.studyList[0].children.studyListEntry; - if ( - studies.length > 1 || - studies[0].children.studyRange || - studies[0].children.StudyPath || - studies[0].children.Comma - ) { - def.type = AUTOMATOR_VAR_TYPES.STUDIES; - def.value = this.visit(ctx.studyList); - return; - } - - // We assume the value is a number; in some cases, we might overwrite it if we see - // this variable used in studies - def.value = new Decimal(studies[0].children.NumberLiteral[0].image); - if (!/^[1-9][0-9]*[1-9]$/u.test(studies[0].children.NumberLiteral[0].image)) { - // Study numbers are pretty specific number patterns - def.type = AUTOMATOR_VAR_TYPES.NUMBER; - } - } - studyRange(ctx, studiesOut) { if (!ctx.firstStudy || ctx.firstStudy[0].isInsertedInRecovery || !ctx.lastStudy || ctx.lastStudy[0].isInsertedInRecovery) { @@ -347,8 +342,12 @@ import { AutomatorLexer } from "./lexer.js"; if (ctx.NumberLiteral) { ctx.$value = new Decimal(ctx.NumberLiteral[0].image); } else if (ctx.Identifier) { + if (!this.isValidVarFormat(ctx.Identifier[0], AUTOMATOR_VAR_TYPES.NUMBER)) { + this.addError(ctx, `Constant ${ctx.Identifier[0].image} cannot be used for comparison`, + `Ensure that ${ctx.Identifier[0].image} contains a properly-formatted number and not a Time Study string`); + } const varLookup = this.lookupVar(ctx.Identifier[0], AUTOMATOR_VAR_TYPES.NUMBER); - if (varLookup) ctx.$value = varLookup.value; + if (varLookup) ctx.$value = ctx.Identifier[0].image; } } @@ -364,7 +363,7 @@ import { AutomatorLexer } from "./lexer.js"; } const T = AutomatorLexer.tokenMap; if (ctx.ComparisonOperator[0].tokenType === T.OpEQ || ctx.ComparisonOperator[0].tokenType === T.EqualSign) { - this.addError(ctx, "Please use an inequality comparison (>,<,>=,<=)", + this.addError(ctx, "Please use an inequality comparison (>, <, >=, <=)", "Comparisons cannot be done with equality, only with inequality operators"); } } @@ -438,9 +437,12 @@ import { AutomatorLexer } from "./lexer.js"; } comparison(ctx) { - const getters = ctx.compareValue.map(cv => ( - cv.children.AutomatorCurrency ? cv.children.AutomatorCurrency[0].tokenType.$getter : () => cv.children.$value - )); + const getters = ctx.compareValue.map(cv => { + if (cv.children.AutomatorCurrency) return cv.children.AutomatorCurrency[0].tokenType.$getter; + const val = cv.children.$value; + if (typeof val === "string") return () => player.reality.automator.constants[val]; + return () => val; + }); const compareFun = ctx.ComparisonOperator[0].tokenType.$compare; return () => compareFun(getters[0](), getters[1]()); } @@ -469,48 +471,34 @@ import { AutomatorLexer } from "./lexer.js"; // eslint-disable-next-line no-loop-func this[cmd.id] = (ctx, output) => { if (ownMethod && ownMethod !== super[cmd.id]) ownMethod.call(this, ctx, output); - const block = blockify(ctx, this); - output.push({ - ...block, - id: UIID.next() - }); + try { + const block = blockify(ctx, this); + output.push({ + ...block, + id: UIID.next() + }); + } catch { + // If a command is invalid, it will throw an exception in blockify and fail to assign a value to block + // We can't, generally, make good guesses to fill in any missing values in order to avoid the exception, + // so we instead just ignore that block + } }; } this.validateVisitor(); } comparison(ctx) { - const isCurrency = ctx.compareValue.map(cv => Boolean(cv.children.AutomatorCurrency)); - // eslint-disable-next-line no-bitwise - if (!(isCurrency[0] ^ isCurrency[1])) { - throw new Error("arbitrary comparisons are not supported in block mode yet"); - } - const currencyIndex = isCurrency[0] ? 0 : 1; - const flipped = currencyIndex === 1; - const valueChildren = ctx.compareValue[1 - currencyIndex].children; - const isDecimalValue = Boolean(valueChildren.$value); - const value = isDecimalValue ? valueChildren.$value.toString() : valueChildren.NumberLiteral[0].image; - let operator = ctx.ComparisonOperator[0].image; - if (flipped) { - switch (operator) { - case ">": - operator = "<"; - break; - case "<": - operator = ">"; - break; - case ">=": - operator = "<="; - break; - case "<=": - operator = ">="; - break; - } - } + const parseInput = index => { + const comp = ctx.compareValue[index]; + const isCurrency = Boolean(comp.children.AutomatorCurrency); + if (isCurrency) return comp.children.AutomatorCurrency[0].image; + return comp.children.$value; + }; + return { - target: ctx.compareValue[currencyIndex].children.AutomatorCurrency[0].image, - secondaryTarget: operator, - inputValue: value, + compOperator: ctx.ComparisonOperator[0].image, + genericInput1: parseInput(0), + genericInput2: parseInput(1), }; } @@ -532,18 +520,10 @@ import { AutomatorLexer } from "./lexer.js"; function compile(input, validateOnly = false) { // The lexer and codemirror choke on the last line of the script, so we pad it with an invisible newline const script = `${input}\n `; - const lexResult = AutomatorLexer.lexer.tokenize(script); - const tokens = lexResult.tokens; - parser.input = tokens; - const parseResult = parser.script(); const validator = new Validator(script); - validator.visit(parseResult); - validator.addLexerErrors(lexResult.errors); - validator.addParserErrors(parser.errors, tokens); - validator.modifyErrorMessages(); let compiled; - if (validator.errors.length === 0 && !validateOnly) { - compiled = new Compiler().visit(parseResult); + if (validator.errorCount === 0 && !validateOnly) { + compiled = new Compiler().visit(validator.parseResult); } return { errors: validator.errors, @@ -553,33 +533,49 @@ import { AutomatorLexer } from "./lexer.js"; AutomatorGrammar.compile = compile; function blockifyTextAutomator(input) { - const lexResult = AutomatorLexer.lexer.tokenize(input); - const tokens = lexResult.tokens; - - AutomatorGrammar.parser.input = tokens; - const parseResult = AutomatorGrammar.parser.script(); const validator = new Validator(input); - validator.visit(parseResult); - if (lexResult.errors.length === 0 && AutomatorGrammar.parser.errors.length === 0 && validator.errors.length === 0) { - const b = new Blockifier(); - const blocks = b.visit(parseResult); - return blocks; - } + const blockifier = new Blockifier(); + const blocks = blockifier.visit(validator.parseResult); - return null; + // The Validator grabs all the lines from the visible script, but the Blockifier will fail to visit any lines + // associated with unparsable commands. This results in a discrepancy in line count whenever a line can't be + // parsed as a specific command, and in general this is a problem we can't try to guess a fix for, so we just + // don't convert it at all. In both cases nested commands are stored recursively, but with different structure. + const validatedCount = entry => { + if (!entry) return 0; + const commandDepth = entry.children; + let foundChildren = 0; + // Inner nested commands are found within a prop given the same name as the command itself - this should only + // actually evaluate to nonzero for at most one key, and will be undefined for all others + for (const key of Object.keys(commandDepth)) { + const nestedBlock = commandDepth[key][0]?.children?.block; + const nestedCommands = nestedBlock ? nestedBlock[0].children.command : []; + foundChildren += nestedCommands + ? nestedCommands.map(c => validatedCount(c) + 1).reduce((sum, val) => sum + val, 0) + : 0; + + // Trailing newlines get turned into a command with a single EOF argument; we return -1 because one level up + // on the recursion this looks like an otherwise valid command and would be counted as such + if (key === "EOF") return -1; + } + return foundChildren; + }; + const visitedCount = block => { + if (!block.nest) return 1; + return 1 + block.nest.map(b => visitedCount(b)).reduce((sum, val) => sum + val, 0); + }; + // Note: top-level structure is slightly different than the nesting structure + const validatedBlocks = validator.parseResult.children.block[0].children.command + .map(c => validatedCount(c) + 1) + .reduce((sum, val) => sum + val, 0); + const visitedBlocks = blocks.map(b => visitedCount(b)).reduce((sum, val) => sum + val, 0); + + return { blocks, validatedBlocks, visitedBlocks }; } AutomatorGrammar.blockifyTextAutomator = blockifyTextAutomator; function validateLine(input) { - const lexResult = AutomatorLexer.lexer.tokenize(input); - const tokens = lexResult.tokens; - AutomatorGrammar.parser.input = tokens; - const parseResult = AutomatorGrammar.parser.script(); const validator = new Validator(input); - validator.visit(parseResult); - validator.addLexerErrors(lexResult.errors); - validator.addParserErrors(parser.errors, tokens); - validator.modifyErrorMessages(); return validator; } diff --git a/javascripts/core/automator/index.js b/javascripts/core/automator/index.js index 72759cf64..1dc0e7bae 100644 --- a/javascripts/core/automator/index.js +++ b/javascripts/core/automator/index.js @@ -1,5 +1,5 @@ -import "./compiler.js"; -import "./automator-codemirror.js"; +import "./compiler"; +import "./automator-codemirror"; -export { AutomatorGrammar } from "./parser.js"; -export { standardizeAutomatorCurrencyName } from "./lexer.js"; +export { AutomatorGrammar } from "./parser"; +export { forbiddenConstantPatterns, standardizeAutomatorValues } from "./lexer"; diff --git a/javascripts/core/automator/lexer.js b/javascripts/core/automator/lexer.js index e71f09d46..9523177b9 100644 --- a/javascripts/core/automator/lexer.js +++ b/javascripts/core/automator/lexer.js @@ -2,7 +2,8 @@ /* eslint-disable require-unicode-regexp */ /* eslint-disable camelcase */ import { createToken, Lexer } from "chevrotain"; -import { DC } from "../constants.js"; + +import { DC } from "../constants"; export const AutomatorLexer = (() => { const createCategory = name => createToken({ name, pattern: Lexer.NA, longer_alt: Identifier }); @@ -70,7 +71,6 @@ export const AutomatorLexer = (() => { const PrestigeEvent = createCategory("PrestigeEvent"); const StudyPath = createCategory("StudyPath"); const TimeUnit = createCategory("TimeUnit"); - const TTCurrency = createCategory("TTCurrency"); createInCategory(ComparisonOperator, "OpGTE", />=/, { $autocomplete: ">=", @@ -102,17 +102,14 @@ export const AutomatorLexer = (() => { EqualSign.$compare = (a, b) => Decimal.eq(a, b); createInCategory(AutomatorCurrency, "EP", /ep/i, { - extraCategories: [TTCurrency], $buyTT: () => TimeTheorems.buyOne(true, "ep"), $getter: () => Currency.eternityPoints.value }); createInCategory(AutomatorCurrency, "IP", /ip/i, { - extraCategories: [TTCurrency], $buyTT: () => TimeTheorems.buyOne(true, "ip"), $getter: () => Currency.infinityPoints.value }); createInCategory(AutomatorCurrency, "AM", /am/i, { - extraCategories: [TTCurrency], $buyTT: () => TimeTheorems.buyOne(true, "am"), $getter: () => Currency.antimatter.value }); @@ -146,7 +143,7 @@ export const AutomatorLexer = (() => { $getter: () => (isRealityAvailable() ? MachineHandler.gainedRealityMachines : DC.D0) }); createInCategory(AutomatorCurrency, "PendingGlyphLevel", /pending[ \t]+glyph[ \t]+level/i, { - $autocomplete: "pending glyph level", + $autocomplete: "pending Glyph level", $getter: () => new Decimal(isRealityAvailable() ? gainedGlyphLevel().actualLevel : 0), }); @@ -274,24 +271,23 @@ export const AutomatorLexer = (() => { createKeyword("Auto", /auto/i); createKeyword("Buy", /buy/i); - createKeyword("Blob", /blob\s\s/i); - createKeyword("Define", /define/i); + // Necessary to hide it from Codemirror's tab auto-completion + createKeyword("Blob", /blob\s\s/i, { + $unlocked: () => false, + }); createKeyword("If", /if/i); createKeyword("Load", /load/i); - createKeyword("Max", /max/i); - createKeyword("All", /all/i, { - extraCategories: [TTCurrency], - $buyTT: () => TimeTheorems.buyOneOfEach(), - }); createKeyword("Notify", /notify/i); createKeyword("Nowait", /nowait/i); createKeyword("Off", /off/i); createKeyword("On", /on/i); createKeyword("Pause", /pause/i); - // Presets are a little special, because they can be named anything (like ec12 or wait) + // Names are a little special, because they can be named anything (like ec12 or wait) // So, we consume the label at the same time as we consume the preset. In order to report - // errors, we also match just the word preset. And, we have to not match comments. - createKeyword("Preset", /preset([ \t]+(\/(?!\/)|[^\n#/])*)?/i); + // errors, we also match just the word name. And, we have to not match comments. + createKeyword("Name", /name([ \t]+(\/(?!\/)|[^\n#/])*)?/i); + createKeyword("Id", /id\b([ \t]+\d)?/i); + createKeyword("Purchase", /purchase/i); createKeyword("Respec", /respec/i); createKeyword("Restart", /restart/i); createKeyword("Start", /start/i); @@ -303,9 +299,11 @@ export const AutomatorLexer = (() => { createKeyword("While", /while/i); createKeyword("BlackHole", /black[ \t]+hole/i, { $autocomplete: "black hole", + $unlocked: () => BlackHole(1).isUnlocked, }); - createKeyword("StoreTime", /stored?[ \t]+time/i, { - $autocomplete: "store time", + createKeyword("StoreGameTime", /stored?[ \t]+game[ \t]+time/i, { + $autocomplete: "store game time", + $unlocked: () => Enslaved.isUnlocked, }); createKeyword("Dilation", /dilation/i); @@ -339,7 +337,6 @@ export const AutomatorLexer = (() => { Keyword, ...keywordTokens, PrestigeEvent, ...tokenLists.PrestigeEvent, StudyPath, ...tokenLists.StudyPath, - TTCurrency, TimeUnit, ...tokenLists.TimeUnit, Identifier, ]; @@ -366,27 +363,44 @@ export const AutomatorLexer = (() => { const automatorCurrencyNames = tokenLists.AutomatorCurrency.map(i => i.$autocomplete.toUpperCase()); - const standardizeAutomatorCurrencyName = function(x) { - // This first line exists for this function to usually return quickly; - // otherwise it's called enough to cause lag. - if (automatorCurrencyNames.includes(x.toUpperCase())) return x.toUpperCase(); + const standardizeAutomatorValues = function(x) { + try { + if (automatorCurrencyNames.includes(x.toUpperCase())) return x.toUpperCase(); + } catch { + // This only happens if the input is a number or Decimal, in which case we don't attempt to change any formatting + // and simply return + return x; + } for (const i of tokenLists.AutomatorCurrency) { // Check for a match of the full string. if (x.match(i.PATTERN) && x.match(i.PATTERN)[0].length === x.length) { return i.$autocomplete.toUpperCase(); } } - // If we get to this point something has gone wrong, a currency name didn't match any of the currency regexps. - throw new Error(`${x} does not seem to be an automator currency`); + // If we get to this point, we haven't matched a currency name and instead assume it's a defined constant and + // return it without any format changes since these are case-sensitive + return x; }; + // In order to disallow individual words within command key words/phrases, we need to ignore certain patterns (mostly + // ones with special regex characters), split the rest of them up across all spaces and tabs, and then flatten the + // final resulting array. Note that this technically duplicates words present in multiple phrases (eg. "pending") + const ignoredPatterns = ["Identifier", "LCurly", "RCurly"]; + const forbiddenConstantPatterns = lexer.lexerDefinition + .filter(p => !ignoredPatterns.includes(p.name)) + .map(p => p.PATTERN.source) + .flatMap(p => ((p.includes("(") || p.includes(")")) ? p : p.split("[ \\t]+"))); + return { lexer, tokens: automatorTokens, tokenIds, tokenMap, - standardizeAutomatorCurrencyName, + standardizeAutomatorValues, + forbiddenConstantPatterns, }; })(); -export const standardizeAutomatorCurrencyName = AutomatorLexer.standardizeAutomatorCurrencyName; +export const standardizeAutomatorValues = AutomatorLexer.standardizeAutomatorValues; + +export const forbiddenConstantPatterns = AutomatorLexer.forbiddenConstantPatterns; diff --git a/javascripts/core/automator/parser.js b/javascripts/core/automator/parser.js index 32bac32f7..714554a2f 100644 --- a/javascripts/core/automator/parser.js +++ b/javascripts/core/automator/parser.js @@ -1,6 +1,7 @@ -import { Parser, EOF } from "chevrotain"; -import { AutomatorCommands } from "./automator-commands.js"; -import { AutomatorLexer } from "./lexer.js"; +import { EOF, Parser } from "chevrotain"; + +import { AutomatorCommands } from "./automator-commands"; +import { AutomatorLexer } from "./lexer"; export const AutomatorGrammar = (function() { const T = AutomatorLexer.tokenMap; diff --git a/javascripts/core/automator/script-templates.js b/javascripts/core/automator/script-templates.js index 720ff0064..99bb1d933 100644 --- a/javascripts/core/automator/script-templates.js +++ b/javascripts/core/automator/script-templates.js @@ -63,11 +63,13 @@ export class ScriptTemplate { storeTreeData(params) { const nowaitStr = params.treeNowait ? " nowait" : ""; if (params.treePreset) { - const presetObj = player.timestudy.presets.find(p => p.name === params.treePreset); - this.storedTreeStr = `studies${nowaitStr} load preset ${presetObj.name}`; + const presetObj = player.timestudy.presets.map((p, i) => ({ ...p, id: i + 1 })) + .find(p => (p.name === params.treePreset || p.id === Number(params.treePreset))); + const preset = presetObj.name ? `name ${presetObj.name}` : `id ${presetObj.id}`; + this.storedTreeStr = `studies${nowaitStr} load ${preset}`; this.storedTreeObj = new TimeStudyTree(presetObj.studies); } else { - this.storedTreeStr = `studies${nowaitStr} ${params.treeStudies}`; + this.storedTreeStr = `studies${nowaitStr} purchase ${params.treeStudies}`; this.storedTreeObj = new TimeStudyTree(params.treeStudies); } if (this.storedTreeObj.invalidStudies.length > 0) this.warnings.push("Tree contains invalid Study IDs"); @@ -233,7 +235,7 @@ export class ScriptTemplate { } this.lines.push(`auto infinity off`); this.lines.push(`auto eternity ${this.parseAutobuyerProp(params.autoEterMode, params.autoEterValue)}`); - this.lines.push(`while tt < ${this.format(TimeStudy.dilation.totalTimeTheoremRequirement)} {`); + this.lines.push(`while total tt < ${this.format(TimeStudy.dilation.totalTimeTheoremRequirement)} {`); this.lines.push(` ${this.storedTreeStr}`); this.lines.push(" studies respec"); this.lines.push(" wait eternity"); diff --git a/javascripts/core/big_crunch.js b/javascripts/core/big_crunch.js index 48fdf30e8..fe868c355 100644 --- a/javascripts/core/big_crunch.js +++ b/javascripts/core/big_crunch.js @@ -1,12 +1,8 @@ -import { GameMechanicState, SetPurchasableMechanicState, RebuyableMechanicState } from "./game-mechanics/index.js"; -import { DC } from "./constants.js"; -import { SpeedrunMilestones } from "./speedrun.js"; +import { DC } from "./constants"; +import FullScreenAnimationHandler from "./full-screen-animation-handler"; export function bigCrunchAnimation() { - document.body.style.animation = "implode 2s 1"; - setTimeout(() => { - document.body.style.animation = ""; - }, 2000); + FullScreenAnimationHandler.display("a-implode", 2); } function handleChallengeCompletion() { @@ -23,9 +19,21 @@ function handleChallengeCompletion() { } } +export function manualBigCrunchResetRequest() { + if (!Player.canCrunch) return; + if (GameEnd.creditsEverClosed) return; + // Before the player has broken infinity, the confirmation modal should never be shown + if ((player.break || PlayerProgress.eternityUnlocked()) && + player.options.confirmations.bigCrunch) { + Modal.bigCrunch.show(); + } else { + bigCrunchResetRequest(); + } +} + export function bigCrunchResetRequest(disableAnimation = false) { if (!Player.canCrunch) return; - if (!disableAnimation && player.options.animations.bigCrunch && document.body.style.animation === "") { + if (!disableAnimation && player.options.animations.bigCrunch && !FullScreenAnimationHandler.isDisplaying) { bigCrunchAnimation(); setTimeout(bigCrunchReset, 1000); } else { @@ -36,7 +44,6 @@ export function bigCrunchResetRequest(disableAnimation = false) { export function bigCrunchReset() { if (!Player.canCrunch) return; - const firstInfinity = !PlayerProgress.infinityUnlocked(); EventHub.dispatch(GAME_EVENT.BIG_CRUNCH_BEFORE); bigCrunchUpdateStatistics(); @@ -45,17 +52,13 @@ export function bigCrunchReset() { Currency.infinityPoints.add(infinityPoints); Currency.infinities.add(gainedInfinities().round()); - bigCrunchTabChange(firstInfinity); + bigCrunchTabChange(!PlayerProgress.infinityUnlocked()); bigCrunchResetValues(); bigCrunchCheckUnlocks(); if (Pelle.isDoomed) PelleStrikes.infinity.trigger(); EventHub.dispatch(GAME_EVENT.BIG_CRUNCH_AFTER); - if (firstInfinity && !Pelle.isDoomed) Modal.message.show(`Upon Infinity, all Dimensions, Dimension Boosts, and Antimatter - Galaxies are reset, but in return, you gain an Infinity Point (IP). This allows you to buy multiple upgrades that - you can find in the Infinity tab. You will also gain one Infinity, which is the stat shown in the Statistics - tab.`); } function bigCrunchUpdateStatistics() { @@ -152,311 +155,6 @@ export function secondSoftReset(forcedNDReset = false) { AchievementTimers.marathon2.reset(); } -class ChargedInfinityUpgradeState extends GameMechanicState { - constructor(config, upgrade) { - super(config); - this._upgrade = upgrade; - } - - get isEffectActive() { - return this._upgrade.isBought && this._upgrade.isCharged; - } -} - -export class InfinityUpgrade extends SetPurchasableMechanicState { - constructor(config, requirement) { - super(config); - if (Array.isArray(requirement) || typeof requirement === "function") { - this._requirements = requirement; - } else if (requirement === undefined) { - this._requirements = []; - } else { - this._requirements = [requirement]; - } - if (config.charged) { - this._chargedEffect = new ChargedInfinityUpgradeState(config.charged, this); - } - } - - get currency() { - return Currency.infinityPoints; - } - - get set() { - return player.infinityUpgrades; - } - - get isAvailableForPurchase() { - return typeof this._requirements === "function" ? this._requirements() - : this._requirements.every(x => x.isBought); - } - - get isEffectActive() { - return this.isBought && !this.isCharged; - } - - get chargedEffect() { - return this._chargedEffect; - } - - purchase() { - if (super.purchase()) { - // This applies the 4th column of infinity upgrades retroactively - if (this.config.id.includes("skip")) skipResetsIfPossible(); - EventHub.dispatch(GAME_EVENT.INFINITY_UPGRADE_BOUGHT); - return true; - } - if (this.canCharge) { - this.charge(); - return true; - } - return false; - } - - get hasChargeEffect() { - return this.config.charged !== undefined; - } - - get isCharged() { - return player.celestials.ra.charged.has(this.id); - } - - get canCharge() { - return this.isBought && - this.hasChargeEffect && - !this.isCharged && - Ra.chargesLeft !== 0 && - !Pelle.isDisabled("chargedInfinityUpgrades"); - } - - charge() { - player.celestials.ra.charged.add(this.id); - } - - disCharge() { - player.celestials.ra.charged.delete(this.id); - } -} - -export function totalIPMult() { - if (Effarig.isRunning && Effarig.currentStage === EFFARIG_STAGES.INFINITY) { - return DC.D1; - } - let ipMult = DC.D1 - .times(ShopPurchase.IPPurchases.currentMult) - .timesEffectsOf( - TimeStudy(41), - TimeStudy(51), - TimeStudy(141), - TimeStudy(142), - TimeStudy(143), - Achievement(85), - Achievement(93), - Achievement(116), - Achievement(125), - Achievement(141).effects.ipGain, - InfinityUpgrade.ipMult, - DilationUpgrade.ipMultDT, - GlyphEffect.ipMult - ); - ipMult = ipMult.times(Replicanti.amount.powEffectOf(AlchemyResource.exponential)); - return ipMult; -} - -export function disChargeAll() { - const upgrades = [ - InfinityUpgrade.totalTimeMult, - InfinityUpgrade.dim18mult, - InfinityUpgrade.dim36mult, - InfinityUpgrade.resetBoost, - InfinityUpgrade.buy10Mult, - InfinityUpgrade.dim27mult, - InfinityUpgrade.dim45mult, - InfinityUpgrade.galaxyBoost, - InfinityUpgrade.thisInfinityTimeMult, - InfinityUpgrade.unspentIPMult, - InfinityUpgrade.dimboostMult, - InfinityUpgrade.ipGen - ]; - for (const upgrade of upgrades) { - if (upgrade.isCharged) { - upgrade.disCharge(); - } - } - player.celestials.ra.disCharge = false; -} - -(function() { - const db = GameDatabase.infinity.upgrades; - const upgrade = (config, requirement) => new InfinityUpgrade(config, requirement); - InfinityUpgrade.totalTimeMult = upgrade(db.totalTimeMult); - InfinityUpgrade.dim18mult = upgrade(db.dim18mult, InfinityUpgrade.totalTimeMult); - InfinityUpgrade.dim36mult = upgrade(db.dim36mult, InfinityUpgrade.dim18mult); - InfinityUpgrade.resetBoost = upgrade(db.resetBoost, InfinityUpgrade.dim36mult); - - InfinityUpgrade.buy10Mult = upgrade(db.buy10Mult); - InfinityUpgrade.dim27mult = upgrade(db.dim27mult, InfinityUpgrade.buy10Mult); - InfinityUpgrade.dim45mult = upgrade(db.dim45mult, InfinityUpgrade.dim27mult); - InfinityUpgrade.galaxyBoost = upgrade(db.galaxyBoost, InfinityUpgrade.dim45mult); - - InfinityUpgrade.thisInfinityTimeMult = upgrade(db.thisInfinityTimeMult); - InfinityUpgrade.unspentIPMult = upgrade(db.unspentIPMult, InfinityUpgrade.thisInfinityTimeMult); - InfinityUpgrade.dimboostMult = upgrade(db.dimboostMult, InfinityUpgrade.unspentIPMult); - InfinityUpgrade.ipGen = upgrade(db.ipGen, InfinityUpgrade.dimboostMult); - - InfinityUpgrade.skipReset1 = upgrade(db.skipReset1); - InfinityUpgrade.skipReset2 = upgrade(db.skipReset2, InfinityUpgrade.skipReset1); - InfinityUpgrade.skipReset3 = upgrade(db.skipReset3, InfinityUpgrade.skipReset2); - InfinityUpgrade.skipResetGalaxy = upgrade(db.skipResetGalaxy, InfinityUpgrade.skipReset3); - - InfinityUpgrade.ipOffline = upgrade(db.ipOffline, () => Achievement(41).isUnlocked); -}()); - -// The repeatable 2xIP upgrade has an odd cost structure - it follows a shallow exponential (step *10) up to e3M, at -// which point it follows a steeper one (step *1e10) up to e6M before finally hardcapping. At the hardcap, there's -// an extra bump that increases the multipler itself from e993k to e1M. All these numbers are specified in -// GameDatabase.infinity.upgrades.ipMult -class InfinityIPMultUpgrade extends GameMechanicState { - get cost() { - if (this.purchaseCount >= this.purchasesAtIncrease) { - return this.config.costIncreaseThreshold - .times(Decimal.pow(this.costIncrease, this.purchaseCount - this.purchasesAtIncrease)); - } - return Decimal.pow(this.costIncrease, this.purchaseCount + 1); - } - - get purchaseCount() { - return player.IPMultPurchases; - } - - get purchasesAtIncrease() { - return this.config.costIncreaseThreshold.log10() - 1; - } - - get hasIncreasedCost() { - return this.purchaseCount >= this.purchasesAtIncrease; - } - - get costIncrease() { - return this.hasIncreasedCost ? 1e10 : 10; - } - - get isCapped() { - return this.cost.gte(this.config.costCap); - } - - get isBought() { - return this.isCapped; - } - - get isRequirementSatisfied() { - return Achievement(41).isUnlocked; - } - - get canBeBought() { - return !Pelle.isDoomed && !this.isCapped && Currency.infinityPoints.gte(this.cost) && this.isRequirementSatisfied; - } - - // This is only ever called with amount = 1 or within buyMax under conditions that ensure the scaling doesn't - // change mid-purchase - purchase(amount = 1) { - if (!this.canBeBought) return; - if (!TimeStudy(181).isBought) { - Autobuyer.bigCrunch.bumpAmount(DC.D2.pow(amount)); - } - Currency.infinityPoints.subtract(Decimal.sumGeometricSeries(amount, this.cost, this.costIncrease, 0)); - player.IPMultPurchases += amount; - GameUI.update(); - } - - buyMax() { - if (!this.canBeBought) return; - if (!this.hasIncreasedCost) { - // Only allow IP below the softcap to be used - const availableIP = Currency.infinityPoints.value.clampMax(this.config.costIncreaseThreshold); - const purchases = Decimal.affordGeometricSeries(availableIP, this.cost, this.costIncrease, 0).toNumber(); - if (purchases <= 0) return; - this.purchase(purchases); - } - // Do not replace it with `if else` - it's specifically designed to process two sides of threshold separately - // (for example, we have 1e4000000 IP and no mult - first it will go to (but not including) 1e3000000 and then - // it will go in this part) - if (this.hasIncreasedCost) { - const availableIP = Currency.infinityPoints.value.clampMax(this.config.costCap); - const purchases = Decimal.affordGeometricSeries(availableIP, this.cost, this.costIncrease, 0).toNumber(); - if (purchases <= 0) return; - this.purchase(purchases); - } - } -} - -InfinityUpgrade.ipMult = new InfinityIPMultUpgrade(GameDatabase.infinity.upgrades.ipMult); - -export class BreakInfinityUpgrade extends SetPurchasableMechanicState { - get currency() { - return Currency.infinityPoints; - } - - get set() { - return player.infinityUpgrades; - } - - onPurchased() { - if (this.id === "postGalaxy") { - SpeedrunMilestones(7).tryComplete(); - PelleStrikes.powerGalaxies.trigger(); - } - } -} - -(function() { - const db = GameDatabase.infinity.breakUpgrades; - const upgrade = props => new BreakInfinityUpgrade(props); - BreakInfinityUpgrade.totalAMMult = upgrade(db.totalAMMult); - BreakInfinityUpgrade.currentAMMult = upgrade(db.currentAMMult); - BreakInfinityUpgrade.galaxyBoost = upgrade(db.galaxyBoost); - - BreakInfinityUpgrade.infinitiedMult = upgrade(db.infinitiedMult); - BreakInfinityUpgrade.achievementMult = upgrade(db.achievementMult); - BreakInfinityUpgrade.slowestChallengeMult = upgrade(db.slowestChallengeMult); - - BreakInfinityUpgrade.infinitiedGen = upgrade(db.infinitiedGen); - BreakInfinityUpgrade.autobuyMaxDimboosts = upgrade(db.autobuyMaxDimboosts); - BreakInfinityUpgrade.autobuyerSpeed = upgrade(db.autobuyerSpeed); -}()); - -class RebuyableBreakInfinityUpgradeState extends RebuyableMechanicState { - get currency() { - return Currency.infinityPoints; - } - - get boughtAmount() { - return player.infinityRebuyables[this.id]; - } - - set boughtAmount(value) { - player.infinityRebuyables[this.id] = value; - } - - get isCapped() { - return this.boughtAmount === this.config.maxUpgrades; - } -} - -BreakInfinityUpgrade.tickspeedCostMult = new class extends RebuyableBreakInfinityUpgradeState { - onPurchased() { - GameCache.tickSpeedMultDecrease.invalidate(); - } -}(GameDatabase.infinity.breakUpgrades.tickspeedCostMult); - -BreakInfinityUpgrade.dimCostMult = new class extends RebuyableBreakInfinityUpgradeState { - onPurchased() { - GameCache.dimensionMultDecrease.invalidate(); - } -}(GameDatabase.infinity.breakUpgrades.dimCostMult); - -BreakInfinityUpgrade.ipGen = new RebuyableBreakInfinityUpgradeState(GameDatabase.infinity.breakUpgrades.ipGen); - export function preProductionGenerateIP(diff) { if (InfinityUpgrade.ipGen.isBought) { const genPeriod = Time.bestInfinity.totalMilliseconds * 10; diff --git a/javascripts/core/black_hole.js b/javascripts/core/black_hole.js index ba33129d3..a9567cfe9 100644 --- a/javascripts/core/black_hole.js +++ b/javascripts/core/black_hole.js @@ -1,5 +1,5 @@ -import { DC } from "./constants.js"; -import { SpeedrunMilestones } from "./speedrun.js"; +import { DC } from "./constants"; +import { SpeedrunMilestones } from "./speedrun"; class BlackHoleUpgradeState { constructor(config) { @@ -33,6 +33,12 @@ class BlackHoleUpgradeState { purchase() { if (!this.isAffordable || this.value === 0) return; + + // Keep the cycle phase consistent before and after purchase so that upgrading doesn't cause weird behavior + // such as immediately activating it when inactive (or worse, skipping past the active segment entirely). + const bh = BlackHole(this.id); + const beforeProg = bh.isCharged ? 1 - bh.stateProgress : bh.stateProgress; + Currency.realityMachines.purchase(this.cost); this.incrementAmount(); this._lazyValue.invalidate(); @@ -40,6 +46,13 @@ class BlackHoleUpgradeState { if (this.onPurchase) { this.onPurchase(); } + + // Adjust the phase to what it was before purchase by changing it directly. This will often result in passing + // in a negative argument to updatePhase(), but this shouldn't cause any problems because it'll never make + // the phase itself negative. In very rare cases this may result in a single auto-pause getting skipped + const stateTime = bh.isCharged ? bh.duration : bh.interval; + bh.updatePhase(stateTime * beforeProg - bh.phase); + EventHub.dispatch(GAME_EVENT.BLACK_HOLE_UPGRADE_BOUGHT); } } @@ -134,7 +147,7 @@ class BlackHoleState { // When inactive, returns time until active; when active, returns time until inactive (or paused for hole 2) get timeToNextStateChange() { - let remainingTime = this.timeWithPreviousActiveToNextStateChange; + const remainingTime = this.timeWithPreviousActiveToNextStateChange; if (this.id === 1) return remainingTime; @@ -143,15 +156,29 @@ class BlackHoleState { if (BlackHole(1).isCharged) return Math.min(remainingTime, BlackHole(1).timeToNextStateChange); return BlackHole(1).timeToNextStateChange; } - if (BlackHole(1).isCharged) { - if (remainingTime < BlackHole(1).timeToNextStateChange) return remainingTime; - remainingTime -= BlackHole(1).timeToNextStateChange; + return BlackHole(1).timeUntilTimeActive(remainingTime); + } + + // Given x, return time it takes for this black hole to get x time active + timeUntilTimeActive(inputTimeActive) { + // Avoid error about reassigning parameter. + let timeActive = inputTimeActive; + if (this.isCharged) { + // We start at the next full activation, so if we have a partial activation + // then that reduces the time required. + // Make sure to handle the case when the current partial activation is enough. + if (timeActive < this.timeToNextStateChange) return timeActive; + // If it's not enough, we can subtract it from our time. + timeActive -= this.timeToNextStateChange; } - let totalTime = BlackHole(1).isCharged - ? BlackHole(1).timeToNextStateChange + BlackHole(1).interval - : BlackHole(1).timeToNextStateChange; - totalTime += Math.floor(remainingTime / BlackHole(1).duration) * BlackHole(1).cycleLength; - totalTime += remainingTime % BlackHole(1).duration; + // Determine the time until the next full activation. + let totalTime = this.isCharged + ? this.timeToNextStateChange + this.interval + : this.timeToNextStateChange; + // This is the number of full cycles needed... + totalTime += Math.floor(timeActive / this.duration) * this.cycleLength; + // And the time from a partial cycle. + totalTime += timeActive % this.duration; return totalTime; } @@ -171,7 +198,7 @@ class BlackHoleState { return ` Pulsing`; } if (Enslaved.isStoringGameTime) { - if (Ra.has(RA_UNLOCKS.ADJUSTABLE_STORED_TIME)) { + if (Ra.unlocks.adjustableStoredTime.canBeApplied) { const storedTimeWeight = player.celestials.enslaved.storedFraction; if (storedTimeWeight !== 0) { return ` Charging (${formatPercents(storedTimeWeight, 1)})`; @@ -221,37 +248,6 @@ class BlackHoleState { // will this cause other bugs? this._data.phase += activePeriod; - // This conditional is a bit convoluted because the more straightforward check of just pausing if it activates - // soon will result in it pausing every tick, including the tick it gets manually unpaused. This is unintuitive - // because it forces the player to change auto-pause modes every time it reaches activation again. Instead, we - // check if before the conditional is false before this tick and true afterwards; this ensures it only ever pauses - // once per cycle, right at the activation threshold. We give it a buffer equal to the acceleration time so that - // it's at full speed once by the time it actually activates. - const beforeTick = this.phase - activePeriod, afterTick = this.phase; - const threhold = this.interval - BlackHoles.ACCELERATION_TIME; - const willActivateOnUnpause = !this.isActive && beforeTick < threhold && afterTick >= threhold; - switch (player.blackHoleAutoPauseMode) { - case BLACK_HOLE_PAUSE_MODE.NO_PAUSE: - break; - case BLACK_HOLE_PAUSE_MODE.PAUSE_BEFORE_BH1: - if (this.id === 1 && willActivateOnUnpause) { - BlackHoles.togglePause(); - GameUI.notify.blackHole(`${RealityUpgrade(20).isBought ? "Black Holes" : "Black Hole"} - automatically paused.`); - return; - } - break; - case BLACK_HOLE_PAUSE_MODE.PAUSE_BEFORE_BH2: - if (willActivateOnUnpause && (this.id === 2 || (this.id === 1 && BlackHole(2).isCharged))) { - BlackHoles.togglePause(); - GameUI.notify.blackHole(`Black Holes automatically paused.`); - return; - } - break; - default: - throw new Error("Unrecognized BH offline pausing mode"); - } - if (this.phase >= this.cycleLength) { // One activation for each full cycle. this._data.activations += Math.floor(this.phase / this.cycleLength); @@ -355,15 +351,22 @@ export const BlackHoles = { Currency.realityMachines.purchase(100); SpeedrunMilestones(17).tryComplete(); Achievement(144).unlock(); + EventHub.dispatch(GAME_EVENT.BLACK_HOLE_UNLOCKED); }, - togglePause: () => { + togglePause: (automatic = false) => { if (!BlackHoles.areUnlocked) return; if (player.blackHolePause) player.requirementChecks.reality.slowestBH = 1; player.blackHolePause = !player.blackHolePause; player.blackHolePauseTime = player.records.realTimePlayed; - const pauseType = BlackHoles.areNegative ? "inverted" : "paused"; - GameUI.notify.blackHole(player.blackHolePause ? `Black Hole ${pauseType}` : "Black Hole unpaused"); + const blackHoleString = RealityUpgrade(20).isBought ? "Black Holes" : "Black Hole"; + // If black holes are going unpaused -> paused, use "inverted" or "paused" depending o + // whether the player's using negative BH (i.e. BH inversion); if going paused -> unpaused, + // use "unpaused". + // eslint-disable-next-line no-nested-ternary + const pauseType = player.blackHolePause ? (BlackHoles.areNegative ? "inverted" : "paused") : 'unpaused'; + const automaticString = automatic ? "automatically " : ""; + GameUI.notify.blackHole(`${blackHoleString} ${automaticString}${pauseType}`); }, get unpauseAccelerationFactor() { @@ -388,12 +391,17 @@ export const BlackHoles = { if (!this.areUnlocked || this.arePaused) return; // This code is intended to successfully update the black hole phases // even for very large values of blackHoleDiff. - const seconds = blackHoleDiff / 1000; - const activePeriods = this.realTimePeriodsWithBlackHoleActive(seconds); + // With auto-pause settings, this code also has to take account of that. + const rawSeconds = blackHoleDiff / 1000; + const [autoPause, seconds] = this.autoPauseData(rawSeconds); + const activePeriods = this.realTimePeriodsWithBlackHoleActive(seconds, true); for (const blackHole of this.list) { if (!blackHole.isUnlocked) break; blackHole.updatePhase(activePeriods[blackHole.id - 1]); } + if (autoPause) { + BlackHoles.togglePause(true); + } }, /** @@ -483,6 +491,7 @@ export const BlackHoles = { const speedupWithoutBlackHole = getGameSpeedupFactor(effectsToConsider); const speedups = [1]; effectsToConsider.push(GAME_SPEED_EFFECT.BLACK_HOLE); + // Crucial thing: this works even if the black holes are paused, it's just that the speedups will be 1. for (const blackHole of this.list) { if (!blackHole.isUnlocked) break; speedups.push(getGameSpeedupFactor(effectsToConsider, blackHole.id) / speedupWithoutBlackHole); @@ -491,7 +500,13 @@ export const BlackHoles = { }, calculateGameTimeFromRealTime(realTime, speedups) { - const effectivePeriods = this.realTimePeriodsWithBlackHoleEffective(realTime, speedups); + // We could do this.autoPauseData(realTime)[1] here but that seems less clear. + // Using _ as an unused variable should be reasonable. + // eslint-disable-next-line no-unused-vars + const [_, realerTime] = this.autoPauseData(realTime); + const effectivePeriods = this.realTimePeriodsWithBlackHoleEffective(realerTime, speedups); + // This adds in time with black holes paused at the end of the list. + effectivePeriods[0] += realTime - realerTime; return effectivePeriods .map((period, i) => period * speedups[i]) .sum(); @@ -535,5 +550,119 @@ export const BlackHoles = { activePeriods.push(activeTime); } return activePeriods; + }, + + /** + * Takes BH number (1 or 2) and number of steps to do in an internal BH simulation. + * Returns real time until we can pause before given BH (i.e., we have a gap of at least 5 seconds before it), + * or null if we can't pause before it. + */ + timeToNextPause(bhNum, steps = 100) { + if (bhNum === 1) { + // This is a simple case that we can do mathematically. + const bh = BlackHole(1); + // If no blackhole gaps are as long as the warmup time, we never pause. + if (bh.interval <= BlackHoles.ACCELERATION_TIME) { + return null; + } + // Find the time until next activation. + const t = (bh.isCharged ? bh.duration : 0) + bh.interval - bh.phase; + // If the time until next activation is less than the acceleration time, + // we have to wait until the activation after that; + // otherwise, we can just use the next activation. + return (t < BlackHoles.ACCELERATION_TIME) + ? t + bh.duration + bh.interval - BlackHoles.ACCELERATION_TIME : t - BlackHoles.ACCELERATION_TIME; + } + // Look at the next 100 black hole transitions. + // This is called every tick if BH pause setting is set to BH2, so we try to optimize it. + // I think the bound of 100 means it can fail only in the case one black hole interval is under 5s + // and the other isn't. In practice, by this point the other interval is usually about 15 seconds + // and both durations are fairly long (a few minutes), making the longest that a gap between activations + // can be 20 seconds (so it's fairly OK not to pause). + // Precalculate some stuff that won't change (or in the case of charged and phases, stuff we'll change ourself + // but just in this simulation) while we call this function. + const charged = [BlackHole(1).isCharged, BlackHole(2).isCharged]; + const phases = [BlackHole(1).phase, BlackHole(2).phase]; + const durations = [BlackHole(1).duration, BlackHole(2).duration]; + const intervals = [BlackHole(1).interval, BlackHole(2).interval]; + // This is technically somewhat incorrect, because assuming durations aren't tiny, the maximum + // possible gap between BH2 activations is the *sum* of the intervals. However, that's still 10 seconds + // if this conditional is true, and pausing the BH because of a 10-second activation gap + // doesn't seem to make much sense. If this is an issue, we could use the sum of the intervals. + // This should also stop this function from being relatively computationally expensive + // if both intervals are 3 seconds (so the next pause would be when they happen to align, + // which is rare and will probably lead to a full 100 steps). + if (intervals[0] <= BlackHoles.ACCELERATION_TIME && intervals[1] <= BlackHoles.ACCELERATION_TIME) { + return null; + } + // Make a list of things to bound phase by. + const phaseBoundList = [[intervals[0]], [durations[0], intervals[1]], [durations[0], durations[1]]]; + // Time tracking. + let inactiveTime = 0; + let totalTime = 0; + for (let i = 0; i < steps; i++) { + // Currently active BH (if BH1 and BH2 are both charged, 2, + // if only BH1 is, 1, if BH1 isn't, 0 regardless of BH2). + // eslint-disable-next-line no-nested-ternary + const current = charged[0] ? (charged[1] ? 2 : 1) : 0; + // Get the list of phase bounds. + const phaseBounds = phaseBoundList[current]; + // Compute time until some phase reaches its bound. + const minTime = current > 0 ? Math.min(phaseBounds[0] - phases[0], phaseBounds[1] - phases[1]) + : phaseBounds[0] - phases[0]; + if (current === 2) { + // Check if there was enough time before this activation to pause. + if (inactiveTime >= BlackHoles.ACCELERATION_TIME) { + return totalTime - BlackHoles.ACCELERATION_TIME; + } + // Not enough time, reset inactive time to 0. + inactiveTime = 0; + } else { + // BH2 is inactive, add to inactive time. + inactiveTime += minTime; + } + // Add to total time in any case. + totalTime += minTime; + // If BH1 is active we should update BH2. + if (current > 0) { + phases[1] += minTime; + if (phases[1] >= phaseBounds[1]) { + charged[1] = !charged[1]; + phases[1] -= phaseBounds[1]; + } + } + // Update BH1 no matter what. + phases[0] += minTime; + if (phases[0] >= phaseBounds[0]) { + charged[0] = !charged[0]; + phases[0] -= phaseBounds[0]; + } + } + // We didn't activate so we return null. + return null; + }, + + /** + * Takes amount of real time. + * Returns 2-item array: + * [will BH be paused in the given amount of real time, real time until pause if so]. + */ + autoPauseData(realTime) { + // This can be called when determining offline time if the black holes are already paused. + // In that case we don't need to pause them (need to pause = false), but they're already paused (0 time). + // This saves us some computation. + if (this.arePaused) return [false, 0]; + if (player.blackHoleAutoPauseMode === BLACK_HOLE_PAUSE_MODE.NO_PAUSE) { + return [false, realTime]; + } + const timeLeft = this.timeToNextPause(player.blackHoleAutoPauseMode); + // Cases in which we don't pause in the given amount of real time: + // null = no pause, (timeLeft < 1e-9) = we auto-paused and there was maybe rounding error, + // now the player's unpaused at this exact point (so we shouldn't pause again), + // (timeLeft > realTime) = we will pause but it'll take longer than the given time. + if (timeLeft === null || timeLeft < 1e-9 || timeLeft > realTime) { + return [false, realTime]; + } + return [true, timeLeft]; } }; diff --git a/javascripts/core/break-infinity-upgrades.js b/javascripts/core/break-infinity-upgrades.js new file mode 100644 index 000000000..fdbaacf19 --- /dev/null +++ b/javascripts/core/break-infinity-upgrades.js @@ -0,0 +1,48 @@ +import { RebuyableMechanicState, SetPurchasableMechanicState } from "./game-mechanics/index"; +import { SpeedrunMilestones } from "./speedrun"; + +export class BreakInfinityUpgradeState extends SetPurchasableMechanicState { + get currency() { + return Currency.infinityPoints; + } + + get set() { + return player.infinityUpgrades; + } + + onPurchased() { + if (this.id === "postGalaxy") { + SpeedrunMilestones(7).tryComplete(); + PelleStrikes.powerGalaxies.trigger(); + } + } +} + +class RebuyableBreakInfinityUpgradeState extends RebuyableMechanicState { + get currency() { + return Currency.infinityPoints; + } + + get boughtAmount() { + return player.infinityRebuyables[this.id]; + } + + set boughtAmount(value) { + player.infinityRebuyables[this.id] = value; + } + + get isCapped() { + return this.boughtAmount === this.config.maxUpgrades; + } + + onPurchased() { + this.config.onPurchased?.(); + } +} + +export const BreakInfinityUpgrade = mapGameDataToObject( + GameDatabase.infinity.breakUpgrades, + config => (config.rebuyable + ? new RebuyableBreakInfinityUpgradeState(config) + : new BreakInfinityUpgradeState(config)) +); diff --git a/javascripts/core/cache.js b/javascripts/core/cache.js index 76a014622..d14aa1ec0 100644 --- a/javascripts/core/cache.js +++ b/javascripts/core/cache.js @@ -82,6 +82,14 @@ export const GameCache = { buyablePerks: new Lazy(() => Perks.all.filter(p => p.canBeBought)), + // Cached because it needs to be checked upon any change to antimatter, but that's a hot path and we want to keep + // unnecessary repetitive calculations and accessing to a minimum + cheapestAntimatterAutobuyer: new Lazy(() => Autobuyer.antimatterDimension.zeroIndexed.concat(Autobuyer.tickspeed) + .filter(ab => !ab.isBought) + .map(ab => ab.antimatterCost.toNumber()) + .min() + ), + // The effect is defined in antimatter_dimensions.js because that's where the non-cached // code originally lived. antimatterDimensionCommonMultiplier: new Lazy(() => antimatterDimensionCommonMultiplier()), @@ -94,8 +102,14 @@ export const GameCache = { timeDimensionCommonMultiplier: new Lazy(() => timeDimensionCommonMultiplier()), + glyphInventorySpace: new Lazy(() => Glyphs.freeInventorySpace), + glyphEffects: new Lazy(() => orderedEffectList.mapToObject(k => k, k => getAdjustedGlyphEffectUncached(k))), + staticGlyphWeights: new Lazy(() => staticGlyphWeights()), + + logTotalGlyphSacrifice: new Lazy(() => GlyphSacrificeHandler.logTotalSacrifice), + totalIPMult: new Lazy(() => totalIPMult()), challengeTimeSum: new Lazy(() => player.challenge.normal.bestTimes.sum()), @@ -104,7 +118,9 @@ export const GameCache = { }; EventHub.logic.on(GAME_EVENT.GLYPHS_CHANGED, () => { + GameCache.glyphInventorySpace.invalidate(); GameCache.glyphEffects.invalidate(); + GameCache.staticGlyphWeights.invalidate(); }, GameCache.glyphEffects); GameCache.antimatterDimensionFinalMultipliers.invalidate = function() { diff --git a/javascripts/core/celestials/V.js b/javascripts/core/celestials/V.js index 600e63032..2ed3f0b0f 100644 --- a/javascripts/core/celestials/V.js +++ b/javascripts/core/celestials/V.js @@ -1,7 +1,9 @@ -import { GameDatabase } from "../secret-formula/game-database.js"; -import { GameMechanicState } from "../game-mechanics/index.js"; -import { CelestialQuotes } from "./quotes.js"; -import { SpeedrunMilestones } from "../speedrun.js"; +import { BitUpgradeState, GameMechanicState } from "../game-mechanics/index"; +import { GameDatabase } from "../secret-formula/game-database"; + +import { SpeedrunMilestones } from "../speedrun"; + +import { Quotes } from "./quotes"; /** * Information about how to format runUnlocks: @@ -25,13 +27,13 @@ class VRunUnlockState extends GameMechanicState { } get canBeReduced() { - return this.completions < this.config.values.length && + return this.completions < this.config.values.length && this.completions !== 0 && new Decimal(this.reduction).neq(this.config.maxShardReduction(this.conditionBaseValue)); } get isReduced() { if (player.celestials.v.goalReductionSteps[this.id] === 0) return false; - return (V.has(V_UNLOCKS.SHARD_REDUCTION) && this.reduction > 0); + return (VUnlocks.shardReduction.canBeApplied && this.reduction > 0); } get reductionCost() { @@ -82,20 +84,55 @@ class VRunUnlockState extends GameMechanicState { this.completions++; GameUI.notify.success(`You have unlocked V-Achievement '${this.config.name}' tier ${this.completions}`); - for (const quote of Object.values(V.quotes)) { - // Quotes without requirements will be shown in other ways - need to check if it exists before calling though - if (quote.requirement && quote.requirement()) { - // TODO If multiple quotes show up simultaneously, this only seems to actually show one of them and skips the - // rest. This might be related to the modal stacking issue - V.quotes.show(quote); + V.updateTotalRunUnlocks(); + + for (const quote of V.quotes.all) { + // Quotes without requirements will be shown in other ways + if (quote.requirement) { + quote.show(); } } - - V.updateTotalRunUnlocks(); } } } +class VUnlockState extends BitUpgradeState { + get bits() { return player.celestials.v.unlockBits; } + set bits(value) { player.celestials.v.unlockBits = value; } + + get pelleDisabled() { + return Pelle.isDoomed && this !== VUnlocks.vAchievementUnlock; + } + + get isEffectActive() { + return this.isUnlocked && !this.pelleDisabled; + } + + get description() { + return typeof this.config.description === "function" ? this.config.description() + : this.config.description; + } + + get rewardText() { + return typeof this.config.reward === "function" ? this.config.reward() + : this.config.reward; + } + + get canBeUnlocked() { + return this.config.requirement() && !this.isUnlocked; + } + + get formattedEffect() { + if (!this.config.effect || !this.config.format) return ""; + + return this.config.format(this.effectValue); + } + + onUnlock() { + GameUI.notify.success(this.description); + } +} + /** * @param {number} id * @return {VRunUnlockState} @@ -109,75 +146,19 @@ export const VRunUnlocks = { all: VRunUnlock.index.compact(), }; -export const V_UNLOCKS = { - V_ACHIEVEMENT_UNLOCK: { - id: 0, - reward: "Unlock V, The Celestial Of Achievements", - description: "Meet all the above requirements simultaneously", - requirement: () => Object.values(GameDatabase.celestials.v.mainUnlock).every(e => e.progress() >= 1) - }, - SHARD_REDUCTION: { - id: 1, - reward: () => `You can spend Perk Points to reduce the goal requirement of all tiers of each V-Achievement.`, - get description() { return `Have ${formatInt(2)} V-Achievements`; }, - requirement: () => V.spaceTheorems >= 2 - }, - ND_POW: { - id: 2, - reward: "Antimatter Dimension power based on total Space Theorems.", - get description() { return `Have ${formatInt(5)} V-Achievements`; }, - effect: () => 1 + Math.sqrt(V.spaceTheorems) / 100, - format: x => formatPow(x, 3, 3), - requirement: () => V.spaceTheorems >= 5 - }, - FAST_AUTO_EC: { - id: 3, - reward: "Achievement multiplier reduces Auto-EC completion time.", - get description() { return `Have ${formatInt(10)} V-Achievements`; }, - effect: () => Achievements.power, - // Base rate is 60 ECs at 20 minutes each - format: x => (Ra.has(RA_UNLOCKS.AUTO_RU_AND_INSTANT_EC) - ? "Instant (Ra upgrade)" - : `${TimeSpan.fromMinutes(60 * 20 / x).toStringShort()} for full completion`), - requirement: () => V.spaceTheorems >= 10 - }, - AUTO_AUTOCLEAN: { - id: 4, - reward: "Unlock the ability to Auto Purge on Reality.", - get description() { return `Have ${formatInt(16)} V-Achievements`; }, - requirement: () => V.spaceTheorems >= 16 - }, - ACHIEVEMENT_BH: { - id: 5, - reward: "Achievement multiplier affects Black Hole power.", - get description() { return `Have ${formatInt(30)} V-Achievements`; }, - effect: () => Achievements.power, - format: x => formatX(x, 2, 0), - requirement: () => V.spaceTheorems >= 30 - }, - RA_UNLOCK: { - id: 6, - get reward() { - return `Reduce the Space Theorem cost of Time Studies by ${formatInt(2)}. - Unlock Ra, Celestial of the Forgotten.`; - }, - get description() { return `Have ${formatInt(36)} V-Achievements`; }, - requirement: () => V.spaceTheorems >= 36 - } -}; +export const VUnlocks = mapGameDataToObject( + GameDatabase.celestials.v.unlocks, + config => new VUnlockState(config) +); export const V = { displayName: "V", + possessiveName: "V's", spaceTheorems: 0, checkForUnlocks() { - for (const key of Object.keys(V_UNLOCKS)) { - const unl = V_UNLOCKS[key]; - if (unl.id === V_UNLOCKS.V_ACHIEVEMENT_UNLOCK.id) continue; - if (unl.requirement() && !this.has(unl)) { - // eslint-disable-next-line no-bitwise - player.celestials.v.unlockBits |= (1 << unl.id); - GameUI.notify.success(unl.description); - } + for (const unl of VUnlocks.all) { + if (unl === VUnlocks.vAchievementUnlock) continue; + unl.unlock(); } if (this.isRunning) { @@ -187,27 +168,22 @@ export const V = { if (this.spaceTheorems >= 36) SpeedrunMilestones(22).tryComplete(); } - if (V.has(V_UNLOCKS.RA_UNLOCK) && !Ra.has(RA_UNLOCKS.AUTO_TP)) { + if (VUnlocks.raUnlock.canBeApplied && !Ra.unlocks.autoTP.canBeApplied) { Ra.checkForUnlocks(); } }, get canUnlockCelestial() { - return V_UNLOCKS.V_ACHIEVEMENT_UNLOCK.requirement(); + return VUnlocks.vAchievementUnlock.canBeUnlocked; }, unlockCelestial() { - // eslint-disable-next-line no-bitwise - player.celestials.v.unlockBits |= (1 << V_UNLOCKS.V_ACHIEVEMENT_UNLOCK.id); - GameUI.notify.success("You have unlocked V, The Celestial Of Achievements!"); - V.quotes.show(V.quotes.UNLOCK); - }, - has(info) { - // eslint-disable-next-line no-bitwise - return Boolean(player.celestials.v.unlockBits & (1 << info.id)); + player.celestials.v.unlockBits |= (1 << VUnlocks.vAchievementUnlock.id); + GameUI.notify.success("You have unlocked V, The Celestial Of Achievements!", 10000); + V.quotes.unlock.show(); }, initializeRun() { clearCelestialRuns(); player.celestials.v.run = true; - this.quotes.show(this.quotes.REALITY_ENTER); + this.quotes.realityEnter.show(); }, updateTotalRunUnlocks() { let sum = 0; @@ -237,7 +213,7 @@ export const V = { return player.celestials.v.run; }, get isFlipped() { - return Ra.has(RA_UNLOCKS.HARD_V); + return Ra.unlocks.unlockHardV.isUnlocked; }, get isFullyCompleted() { return this.spaceTheorems >= 66; @@ -248,100 +224,10 @@ export const V = { nextHardReductionCost(currReductionSteps) { return 1000 * Math.pow(1.15, currReductionSteps); }, - quotes: new CelestialQuotes("v", { - INITIAL: CelestialQuotes.singleLine( - 1, "How pathetic..." - ), - UNLOCK: { - id: 2, - lines: [ - "Welcome to my Reality.", - "I am surprised you could reach it.", - "This is my realm after all...", - "Not everyone is as great as me.", - ] - }, - REALITY_ENTER: { - id: 3, - lines: [ - "Good luck with that!", - "You will need it.", - "My reality is flawless. You will fail.", - ] - }, - REALITY_COMPLETE: { - id: 4, - lines: [ - "So fast...", - "Do not think so much of yourself.", - "This is just the beginning.", - "You will never be better than me.", - ] - }, - ACHIEVEMENT_1: { - id: 5, - requirement: () => V.spaceTheorems >= 1, - lines: [ - "Only one? Pathetic.", - "Your accomplishments pale in comparison to mine.", - ] - }, - ACHIEVEMENT_6: { - id: 6, - requirement: () => V.spaceTheorems >= 6, - lines: [ - "This is nothing.", - "Do not be so full of yourself.", - ] - }, - HEX_1: { - id: 7, - requirement: () => player.celestials.v.runUnlocks.filter(a => a === 6).length >= 1, - lines: [ - "Do not think it will get any easier from now on.", - "You are awfully proud for such a little achievement.", - ] - }, - ACHIEVEMENT_12: { - id: 8, - requirement: () => V.spaceTheorems >= 12, - lines: [ - "How did you...", - "This barely amounts to anything!", - "You will never complete them all.", - ] - }, - ACHIEVEMENT_24: { - id: 9, - requirement: () => V.spaceTheorems >= 24, - lines: [ - "Impossible...", - "After how difficult it was for me...", - ] - }, - HEX_3: { - id: 10, - requirement: () => player.celestials.v.runUnlocks.filter(a => a === 6).length >= 3, - lines: [ - "No... No... No...", - "This cannot be...", - ] - }, - ALL_ACHIEVEMENTS: { - id: 11, - requirement: () => V.spaceTheorems >= 36, - lines: [ - "I... how did you do it...", - "I worked so hard to get them...", - "I am the greatest...", - "No one is better than me...", - "No one... no one... no on-", - ] - } - }), + quotes: Quotes.v, symbol: "⌬" }; EventHub.logic.on(GAME_EVENT.TAB_CHANGED, () => { - if (Tab.celestials.v.isOpen) V.quotes.show(V.quotes.INITIAL); + if (Tab.celestials.v.isOpen) V.quotes.initial.show(); }); diff --git a/javascripts/core/celestials/celestials.js b/javascripts/core/celestials/celestials.js index 398206786..63d2a9926 100644 --- a/javascripts/core/celestials/celestials.js +++ b/javascripts/core/celestials/celestials.js @@ -1,10 +1,10 @@ -import { Teresa } from "./teresa.js"; -import { Effarig } from "./effarig.js"; -import { Enslaved } from "./enslaved.js"; -import { V } from "./V.js"; -import { Ra } from "./ra/ra.js"; -import { Laitela } from "./laitela/laitela.js"; +import { Effarig } from "./effarig"; +import { Enslaved } from "./enslaved"; +import { Laitela } from "./laitela/laitela"; import { Pelle } from "./pelle/pelle"; +import { Ra } from "./ra/ra"; +import { Teresa } from "./teresa"; +import { V } from "./V"; export const Celestials = { teresa: Teresa, @@ -19,59 +19,95 @@ export const Celestials = { GameDatabase.celestials.descriptions = [ { name: "Teresa", - description() { - return `Glyph Time Theorem generation is disabled and\ - you gain less Infinity Points and Eternity Points (x^${format(0.55, 2, 2)}).`; + effects() { + return `Glyph Time Theorem generation is disabled. + You gain less Infinity Points and Eternity Points (x^${format(0.55, 2, 2)}).`; }, }, { name: "Effarig", + effects() { + return `All Dimension multipliers, game speed, and tickspeed are severely lowered, like Dilation. + Infinity Power reduces the production and game speed penalties and Time Shards reduce the tickspeed penalty. + Glyph levels are temporarily capped to ${formatInt(Effarig.glyphLevelCap)}, rarity is unaffected.`; + }, description() { - return `all Dimension multipliers, gamespeed, and tickspeed are severely lowered, like Dilation. - Infinity Power reduces the production and gamespeed penalties and Time Shards reduce the tickspeed penalty. - Glyph levels are temporarily capped${Effarig.isRunning ? ` to ${Effarig.glyphLevelCap}` : ``}, - rarity is unaffected. You will exit Effarig's Reality when you complete a Layer of it for the first time.`; + return `You will exit Effarig's Reality when you complete a Layer of it for the first time.`; } }, { - name: "The Enslaved Ones", - description() { - return `\nGlyph levels will be boosted to a minimum of ${formatInt(5000)} - Infinity, Time, and 8th Antimatter Dimension purchases are limited to ${formatInt(1)} each - Antimatter Dimension multipliers are always Dilated (the Glyph effect still only applies in actual Dilation) - Time Study 192 (uncapped Replicanti) is locked - The Black Hole is disabled - Tachyon Particle production and Dilated Time production are severely reduced - Time Theorem generation from Dilation Glyphs is disabled - Certain challenge goals have been increased - Stored Time is discharged at a reduced effectiveness (exponent^${format(0.55, 2, 2)}) `; + name: "The Nameless Ones", + effects() { + return `Glyph levels are boosted to a minimum of ${formatInt(5000)}. + Infinity, Time, and 8th Antimatter Dimension purchases are limited to ${formatInt(1)} each. + Antimatter Dimension multipliers are always Dilated (the Glyph effect still only applies in actual Dilation). + Time Study 192 (uncapped Replicanti) is locked. + The Black Hole is disabled. + Tachyon Particle production and Dilated Time production are severely reduced. + Time Theorem generation from Dilation Glyphs is disabled. + Certain challenge goals are increased. + Stored game time is discharged at a reduced effectiveness (exponent^${format(0.55, 2, 2)}).`; } }, { name: "V", - description() { - return `all Dimension multipliers, Eternity Point gain, Infinity Point gain, and Dilated Time gain per second\ - are square-rooted, and Replicanti interval is squared.`; + effects() { + const vEffect = `All Dimension multipliers, Eternity Point gain, Infinity Point gain, and Dilated Time gain\ + per second are square-rooted. + The Replicanti interval is squared.`; + const vEffectAdditional = ` + The Exponential Glyph Alchemy effect is disabled.`; + + return Ra.unlocks.unlockGlyphAlchemy.canBeApplied + ? vEffect + vEffectAdditional + : vEffect; } }, { name: "Ra", - description() { - return `you only have ${formatInt(4)} Dimension Boosts and can't gain any more, and the Tickspeed purchase - multiplier is fixed at ${formatX(1.1245, 0, 3)}.\n`; + effects() { + return `You only have ${formatInt(4)} Dimension Boosts and can't gain any more. + The Tickspeed purchase multiplier is fixed at ${formatX(1.1245, 0, 3)}.`; }, }, { name: "Lai'tela", + effects() { + let disabledDims; + const highestActive = 8 - Laitela.difficultyTier; + switch (highestActive) { + case 0: + disabledDims = "all Dimensions"; + break; + case 1: + disabledDims = "2nd and higher Dimensions"; + break; + case 2: + disabledDims = "3rd and higher Dimensions"; + break; + case 7: + disabledDims = "8th Dimensions"; + break; + default: + disabledDims = `${highestActive + 1}th and higher Dimensions`; + break; + } + const disabledText = highestActive === 8 + ? "" + : `Production from ${disabledDims} is disabled.`; + + return `Infinity Point and Eternity Point gain are Dilated. + Game speed is reduced to ${formatInt(1)} and gradually comes back over ${formatInt(10)} minutes. + Black Hole storing, discharging, and pulsing are disabled. + ${disabledText}`; + }, description() { - return `Infinity Point and Eternity Point gain are Dilated.\ - Game speed is reduced to ${formatInt(1)} and gradually comes back over ${formatInt(10)} minutes,\ - and Black Hole storing/discharging/pulsing are disabled.\n - Antimatter generates entropy inside of this Reality.\ + return `Antimatter generates entropy inside of this Reality.\ At ${formatPercents(1)} entropy, the Reality becomes destabilized\ - and you gain a reward based on how quickly you reached ${formatPercents(1)}.\ - If you can destabilize in less than ${formatInt(30)} seconds, the Reality gives a stronger reward,\ - but becomes significantly more difficult.`; + and you gain a reward based on how quickly you reached ${formatPercents(1)}. + Destabilizing the Reality in less than ${formatInt(30)} seconds makes it become significantly more difficult,\ + in exchange for giving a much stronger reward.\ + Doing this ${formatInt(8)} times will also give a ${formatX(8)} to Dark Energy gain.`; } }, diff --git a/javascripts/core/celestials/effarig.js b/javascripts/core/celestials/effarig.js index b560b5f39..8dc5410ad 100644 --- a/javascripts/core/celestials/effarig.js +++ b/javascripts/core/celestials/effarig.js @@ -1,7 +1,9 @@ -import { GameDatabase } from "../secret-formula/game-database.js"; -import { GameMechanicState } from "../game-mechanics/index.js"; -import { CelestialQuotes } from "./quotes.js"; -import { DC } from "../constants.js"; +import { BitUpgradeState } from "../game-mechanics/index"; +import { GameDatabase } from "../secret-formula/game-database"; + +import { DC } from "../constants"; + +import { Quotes } from "./quotes"; export const EFFARIG_STAGES = { INFINITY: 1, @@ -12,16 +14,12 @@ export const EFFARIG_STAGES = { export const Effarig = { displayName: "Effarig", + possessiveName: "Effarig's", initializeRun() { - const isRestarting = player.celestials.effarig.run; clearCelestialRuns(); player.celestials.effarig.run = true; recalculateAllGlyphs(); Tab.reality.glyphs.show(false); - if (!isRestarting) { - Modal.message.show(`Your Glyph levels have been limited to ${Effarig.glyphLevelCap}. Infinity Power - reduces the nerf to multipliers and game speed, and Time Shards reduce the nerf to tickspeed.`); - } }, get isRunning() { return player.celestials.effarig.run; @@ -55,16 +53,14 @@ export const Effarig = { get glyphEffectAmount() { const genEffectBitmask = Glyphs.activeList .filter(g => generatedTypes.includes(g.type)) - // eslint-disable-next-line no-bitwise .reduce((prev, curr) => prev | curr.effects, 0); const nongenEffectBitmask = Glyphs.activeList .filter(g => !generatedTypes.includes(g.type)) - // eslint-disable-next-line no-bitwise .reduce((prev, curr) => prev | curr.effects, 0); return countValuesFromBitmask(genEffectBitmask) + countValuesFromBitmask(nongenEffectBitmask); }, get shardsGained() { - if (!Teresa.has(TERESA_UNLOCKS.EFFARIG)) return 0; + if (!TeresaUnlocks.effarig.canBeApplied) return 0; return Math.floor(Math.pow(Currency.eternityPoints.exponent / 7500, this.glyphEffectAmount)) * AlchemyResource.effarig.effectValue; }, @@ -101,139 +97,44 @@ export const Effarig = { // Will return 0 if Effarig Infinity is uncompleted return Math.floor(replicantiCap().pLog10() / LOG10_MAX_VALUE - 1); }, - quotes: new CelestialQuotes("effarig", { - INITIAL: { - id: 1, - lines: [ - "Welcome to my humble abode.", - "I am Effarig, and I govern Glyphs.", - "I am different from Teresa; not as simplistic as you think.", - "I use the shards of Glyphs to enforce my will.", - "I collect them for the bounty of this realm.", - "What are you waiting for? Get started.", - ] - }, - UNLOCK_WEIGHTS: CelestialQuotes.singleLine( - 2, "Do you like my little shop? It is not much, but it is mine." - ), - UNLOCK_GLYPH_FILTER: CelestialQuotes.singleLine( - 3, "This purchase will help you out." - ), - UNLOCK_SET_SAVES: CelestialQuotes.singleLine( - 4, "Is that too much? I think it is too much." - ), - UNLOCK_RUN: { - id: 5, - lines: [ - "You bought out my entire stock... well, at least I am rich now.", - "The heart of my Reality is suffering. Each Layer is harder than the last.", - "I hope you never complete it.", - ] - }, - COMPLETE_INFINITY: { - id: 6, - lines: [ - "* You have completed Effarig's Infinity.", - "This is the first threshold. It only gets worse from here.", - "None but me know enough about my domain to get further.", - ] - }, - COMPLETE_ETERNITY: { - id: 7, - lines: [ - "* You have completed Effarig's Eternity.", - "This is the limit. I do not want you to proceed past this point.", - "You will not finish this in your lifetime.", - "I will just wait here until you give up.", - ] - }, - COMPLETE_REALITY: { - id: 8, - lines: [ - "* You have completed Effarig's Reality.", - "So this is the diabolical power... what frightened the others...", - "Do you think this was worth it? Trampling on what I have done?", - "And for what purpose? You could have joined, we could have cooperated.", - "But no. It is over. Leave while I cling onto what is left.", - ] - } - }), + quotes: Quotes.effarig, symbol: "Ϙ" }; -class EffarigUnlockState extends GameMechanicState { - constructor(config) { - super(config); - if (this.id < 0 || this.id > 31) throw new Error(`Id ${this.id} out of bit range`); - } +class EffarigUnlockState extends BitUpgradeState { + get bits() { return player.celestials.effarig.unlockBits; } + set bits(value) { player.celestials.effarig.unlockBits = value; } get cost() { return this.config.cost; } - get isUnlocked() { - // eslint-disable-next-line no-bitwise - return Boolean(player.celestials.effarig.unlockBits & (1 << this.id)); - } - - get canBeApplied() { - return this.isUnlocked && !Pelle.isDisabled("effarig"); - } - - unlock() { - // eslint-disable-next-line no-bitwise - player.celestials.effarig.unlockBits |= (1 << this.id); + get isEffectActive() { + return !Pelle.isDisabled("effarig"); } purchase() { if (this.isUnlocked || !Currency.relicShards.purchase(this.cost)) return; this.unlock(); - switch (this) { - case EffarigUnlock.adjuster: - Effarig.quotes.show(Effarig.quotes.UNLOCK_WEIGHTS); - ui.view.tabs.reality.openGlyphWeights = true; - Tab.reality.glyphs.show(); - break; - case EffarigUnlock.glyphFilter: - Effarig.quotes.show(Effarig.quotes.UNLOCK_GLYPH_FILTER); - player.reality.showSidebarPanel = GLYPH_SIDEBAR_MODE.FILTER_SETTINGS; - break; - case EffarigUnlock.setSaves: - Effarig.quotes.show(Effarig.quotes.UNLOCK_SET_SAVES); - player.reality.showSidebarPanel = GLYPH_SIDEBAR_MODE.SAVED_SETS; - break; - case EffarigUnlock.run: - Effarig.quotes.show(Effarig.quotes.UNLOCK_RUN); - break; - default: - throw new Error("Unknown Effarig upgrade"); - } + this.config.onPurchased?.(); } } -export const EffarigUnlock = (function() { - const db = GameDatabase.celestials.effarig.unlocks; - return { - adjuster: new EffarigUnlockState(db.adjuster), - glyphFilter: new EffarigUnlockState(db.glyphFilter), - setSaves: new EffarigUnlockState(db.setSaves), - run: new EffarigUnlockState(db.run), - infinity: new EffarigUnlockState(db.infinity), - eternity: new EffarigUnlockState(db.eternity), - reality: new EffarigUnlockState(db.reality), - }; -}()); +export const EffarigUnlock = mapGameDataToObject( + GameDatabase.celestials.effarig.unlocks, + config => new EffarigUnlockState(config) +); EventHub.logic.on(GAME_EVENT.TAB_CHANGED, () => { - if (Tab.celestials.effarig.isOpen) Effarig.quotes.show(Effarig.quotes.INITIAL); + if (Tab.celestials.effarig.isOpen) Effarig.quotes.initial.show(); }); EventHub.logic.on(GAME_EVENT.BIG_CRUNCH_BEFORE, () => { if (!Effarig.isRunning) return; - Effarig.quotes.show(Effarig.quotes.COMPLETE_INFINITY); + Effarig.quotes.completeInfinity.show(); }); EventHub.logic.on(GAME_EVENT.ETERNITY_RESET_BEFORE, () => { if (!Effarig.isRunning) return; - Effarig.quotes.show(Effarig.quotes.COMPLETE_ETERNITY); + Effarig.quotes.completeEternity.show(); }); diff --git a/javascripts/core/celestials/enslaved.js b/javascripts/core/celestials/enslaved.js index 8ca2dc76b..b6f1fda2c 100644 --- a/javascripts/core/celestials/enslaved.js +++ b/javascripts/core/celestials/enslaved.js @@ -1,6 +1,7 @@ -import { GameDatabase } from "../secret-formula/game-database.js"; -import { GameMechanicState } from "../game-mechanics/index.js"; -import { CelestialQuotes } from "./quotes.js"; +import { BitUpgradeState } from "../game-mechanics/index"; +import { GameDatabase } from "../secret-formula/game-database"; + +import { Quotes } from "./quotes"; export const ENSLAVED_UNLOCKS = { FREE_TICKSPEED_SOFTCAP: { @@ -17,13 +18,14 @@ export const ENSLAVED_UNLOCKS = { const hasRarityRequirement = strengthToRarity(player.records.bestReality.glyphStrength) >= 100; return hasLevelRequirement && hasRarityRequirement; }, - description: () => `Unlock The Enslaved Ones' Reality (requires + description: () => `Unlock The Nameless Ones' Reality (requires a level ${formatInt(5000)} Glyph and a ${formatRarity(100)} rarity Glyph)`, } }; export const Enslaved = { - displayName: "Enslaved", + displayName: "The Nameless Ones", + possessiveName: "The Nameless Ones'", boostReality: false, BROKEN_CHALLENGES: [2, 3, 4, 5, 7, 8, 10, 11, 12], nextTickDiff: 50, @@ -35,36 +37,45 @@ export const Enslaved = { currentBlackHoleStoreAmountPerMs: 0, tachyonNerf: 0.3, toggleStoreBlackHole() { - if (Pelle.isDoomed) return; + if (!this.canModifyGameTimeStorage) return; player.celestials.enslaved.isStoring = !player.celestials.enslaved.isStoring; player.celestials.enslaved.isStoringReal = false; - if (!Ra.has(RA_UNLOCKS.ADJUSTABLE_STORED_TIME)) { + if (!Ra.unlocks.adjustableStoredTime.canBeApplied) { player.celestials.enslaved.storedFraction = 1; } }, toggleStoreReal() { - if (Pelle.isDoomed) return; + if (!this.canModifyRealTimeStorage && !this.isStoredRealTimeCapped) return; player.celestials.enslaved.isStoringReal = !player.celestials.enslaved.isStoringReal; player.celestials.enslaved.isStoring = false; }, toggleAutoStoreReal() { - if (Pelle.isDoomed) return; + if (!this.canModifyRealTimeStorage) return; player.celestials.enslaved.autoStoreReal = !player.celestials.enslaved.autoStoreReal; }, + get canModifyGameTimeStorage() { + return Enslaved.isUnlocked && !Pelle.isDoomed && !BlackHoles.arePaused && !EternityChallenge(12).isRunning && + !Enslaved.isRunning && !Laitela.isRunning; + }, + get canModifyRealTimeStorage() { + return Enslaved.isUnlocked && !Pelle.isDoomed; + }, + get isStoredRealTimeCapped() { + return player.celestials.enslaved.storedReal < this.storedRealTimeCap; + }, + // We assume that the situations where you can't modify time storage settings (of either type) are exactly the cases + // where they have also been explicitly disabled via other game mechanics. This also reduces UI boilerplate code. get isStoringGameTime() { - return Enslaved.isUnlocked && player.celestials.enslaved.isStoring && !BlackHoles.arePaused && - !EternityChallenge(12).isRunning && !Laitela.isRunning; + return this.canModifyGameTimeStorage && player.celestials.enslaved.isStoring; }, get isStoringRealTime() { - return Enslaved.isUnlocked && player.celestials.enslaved.isStoringReal; + return this.canModifyRealTimeStorage && player.celestials.enslaved.isStoringReal; }, get storedRealTimeEfficiency() { return 0.7; }, get storedRealTimeCap() { - const addedCap = Ra.has(RA_UNLOCKS.IMPROVED_STORED_TIME) - ? RA_UNLOCKS.IMPROVED_STORED_TIME.effect.realTimeCap() - : 0; + const addedCap = Ra.unlocks.improvedStoredTime.effects.realTimeCap.effectOrDefault(0); return 1000 * 3600 * 8 + addedCap; }, get isAutoReleasing() { @@ -81,6 +92,8 @@ export const Enslaved = { player.celestials.enslaved.isStoringReal = false; player.celestials.enslaved.storedReal = maxTime; } + // More than 24 hours in milliseconds + if (player.celestials.enslaved.storedReal > (24 * 60 * 60 * 1000)) SecretAchievement(46).unlock(); player.lastUpdate = thisUpdate; }, autoStoreRealTime(diffMs) { @@ -96,9 +109,7 @@ export const Enslaved = { }, // "autoRelease" should only be true when called with the Ra upgrade useStoredTime(autoRelease) { - if (Pelle.isDoomed) return; if (!this.canRelease(autoRelease)) return; - if (EternityChallenge(12).isRunning) return; player.requirementChecks.reality.slowestBH = 1; let release = player.celestials.enslaved.stored; if (Enslaved.isRunning) { @@ -124,7 +135,7 @@ export const Enslaved = { }, buyUnlock(info) { if (!this.canBuy(info)) return false; - if (info.id === ENSLAVED_UNLOCKS.RUN.id) this.quotes.show(this.quotes.UNLOCK_RUN); + if (info.id === ENSLAVED_UNLOCKS.RUN.id) this.quotes.unlockRun.show(); player.celestials.enslaved.stored -= info.price; player.celestials.enslaved.unlocks.push(info.id); return true; @@ -132,16 +143,26 @@ export const Enslaved = { initializeRun() { clearCelestialRuns(); player.celestials.enslaved.run = true; - player.secretUnlocks.viewSecretTS = false; + player.celestials.enslaved.hasSecretStudy = false; this.feltEternity = false; - this.quotes.show(this.quotes.START_RUN); + + // Re-validation needs to be done here because this code gets called after the automator attempts to start. + // This is a special case for Nameless because it's one of the only two cases where a command becomes locked + // again (the other being Pelle entry, which just force-stops the automator entirely). + AutomatorData.recalculateErrors(); + if (AutomatorBackend.state.mode === AUTOMATOR_MODE.RUN && AutomatorData.currentErrors().length) { + AutomatorBackend.stop(); + GameUI.notify.error("This Reality forbids Black Holes! (Automator stopped)"); + } + + this.quotes.startRun.show(); }, get isRunning() { return player.celestials.enslaved.run; }, completeRun() { player.celestials.enslaved.completed = true; - this.quotes.show(this.quotes.COMPLETE_REALITY); + this.quotes.completeReality.show(); }, get isCompleted() { return player.celestials.enslaved.completed; @@ -154,6 +175,9 @@ export const Enslaved = { return Math.max(baseRealityBoostRatio, Math.floor(player.celestials.enslaved.storedReal / Math.max(1000, Time.thisRealityRealTime.totalMilliseconds))); }, + get canAmplify() { + return this.realityBoostRatio > 1 && !Pelle.isDoomed && !isInCelestialReality(); + }, storedTimeInsideEnslaved(stored) { if (stored <= 1e3) return stored; return Math.pow(10, Math.pow(Math.log10(stored / 1e3), 0.55)) * 1e3; @@ -162,7 +186,8 @@ export const Enslaved = { if (!this.feltEternity) { EnslavedProgress.feelEternity.giveProgress(); this.feltEternity = true; - Modal.message.show("Time in Eternity will be scaled by number of Eternities"); + Modal.message.show(`Time in this Eternity will be multiplied by number of Eternities, + up to a maximum of ${formatX(1e66)}.`, { closeEvent: GAME_EVENT.REALITY_RESET_AFTER }, 1); } }, get feltEternity() { @@ -188,101 +213,45 @@ export const Enslaved = { } return true; }, - quotes: new CelestialQuotes("enslaved", { - INITIAL: { - id: 1, - lines: [ - "A visitor? I have not had one... eons.", - "I... had a name. It has been lost... to this place.", - "The others... will not let me rest. I do their work with time...", - "Place time... into places... that need it...", - "Watch myself grow... pass and die.", - "Perhaps you... will break these chains... I will wait.", - ] - }, - UNLOCK_RUN: { - id: 2, - lines: [ - "The others... used me. They will use... or destroy you.", - "End my suffering... power will be yours...", - ] - }, - START_RUN: { - id: 3, - lines: [ - "So little space... but no... prison... is perfect.", - "They squeezed... this Reality... too tightly. Cracks appeared.", - "Search... everywhere. I will help... where I can.", - ] - }, - COMPLETE_REALITY: { - id: 4, - lines: [ - "All... fragments... clones... freed.", - "I have given... tools... of my imprisoning. Use them...", - "Freedom from torture... is torture itself.", - ] - }, - EC6C10: CelestialQuotes.singleLine( - 5, "... did not... underestimate you..." - ), - HINT_UNLOCK: { - id: 6, - lines: [ - "... you need... to look harder...", - "I think... I can help...", - "* You have unlocked help from The Enslaved Ones." - ] - }, - }), - symbol: "" + quotes: Quotes.enslaved, + // Unicode f0c1. + symbol: "\uf0c1" }; -class EnslavedProgressState extends GameMechanicState { - constructor(config) { - super(config); - if (this.id < 0 || this.id > 31) throw new Error(`Id ${this.id} out of bit range`); - } +class EnslavedProgressState extends BitUpgradeState { + get bits() { return player.celestials.enslaved.hintBits; } + set bits(value) { player.celestials.enslaved.hintBits = value; } get hasProgress() { - // eslint-disable-next-line no-bitwise return Boolean(player.celestials.enslaved.progressBits & (1 << this.id)); } get hasHint() { - // eslint-disable-next-line no-bitwise - return this.hasProgress || Boolean(player.celestials.enslaved.hintBits & (1 << this.id)); + return this.hasProgress || this.isUnlocked; + } + + get hintInfo() { + return this.config.hint; + } + + get completedInfo() { + return typeof this.config.condition === "function" ? this.config.condition() : this.config.condition; } giveProgress() { // Bump the last hint time appropriately if the player found the hint if (this.hasHint && !this.hasProgress) { player.celestials.enslaved.zeroHintTime -= Math.log(2) / Math.log(3) * TimeSpan.fromDays(1).totalMilliseconds; - GameUI.notify.success("You found a crack in The Enslaved Ones' Reality!"); + GameUI.notify.success("You found a crack in The Nameless Ones' Reality!", 10000); } - // eslint-disable-next-line no-bitwise player.celestials.enslaved.progressBits |= (1 << this.id); } - - giveHint() { - // eslint-disable-next-line no-bitwise - player.celestials.enslaved.hintBits |= (1 << this.id); - } } -export const EnslavedProgress = (function() { - const db = GameDatabase.celestials.enslaved.progress; - return { - hintsUnlocked: new EnslavedProgressState(db.hintsUnlocked), - ec1: new EnslavedProgressState(db.ec1), - feelEternity: new EnslavedProgressState(db.feelEternity), - ec6: new EnslavedProgressState(db.ec6), - c10: new EnslavedProgressState(db.c10), - secretStudy: new EnslavedProgressState(db.secretStudy), - storedTime: new EnslavedProgressState(db.storedTime), - challengeCombo: new EnslavedProgressState(db.challengeCombo), - }; -}()); +export const EnslavedProgress = mapGameDataToObject( + GameDatabase.celestials.enslaved.progress, + config => new EnslavedProgressState(config) +); export const Tesseracts = { get bought() { @@ -299,6 +268,7 @@ export const Tesseracts = { buyTesseract() { if (!this.canBuyTesseract) return; + if (GameEnd.creditsEverClosed) return; player.celestials.enslaved.tesseracts++; }, @@ -335,5 +305,5 @@ export const Tesseracts = { }; EventHub.logic.on(GAME_EVENT.TAB_CHANGED, () => { - if (Tab.celestials.enslaved.isOpen) Enslaved.quotes.show(Enslaved.quotes.INITIAL); + if (Tab.celestials.enslaved.isOpen) Enslaved.quotes.initial.show(); }); diff --git a/javascripts/core/celestials/laitela/dark-matter-dimension.js b/javascripts/core/celestials/laitela/dark-matter-dimension.js index 98577bdb4..54e3f78f7 100644 --- a/javascripts/core/celestials/laitela/dark-matter-dimension.js +++ b/javascripts/core/celestials/laitela/dark-matter-dimension.js @@ -1,5 +1,5 @@ -import { DimensionState } from "../../dimensions/dimension.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { DimensionState } from "../../dimensions/dimension"; /** * Constants for easily adjusting values @@ -50,8 +50,8 @@ export class DarkMatterDimensionState extends DimensionState { const perUpgrade = INTERVAL_PER_UPGRADE; const tierFactor = Math.pow(4, this.tier - 1); return 1000 * tierFactor * Math.pow(perUpgrade, this.data.intervalUpgrades) * - Math.pow(SingularityMilestone.ascensionIntervalScaling.effectValue, this.ascensions) * - SingularityMilestone.darkDimensionIntervalReduction.effectValue; + Math.pow(SingularityMilestone.ascensionIntervalScaling.effectOrDefault(1200), this.ascensions) * + SingularityMilestone.darkDimensionIntervalReduction.effectOrDefault(1); } get interval() { @@ -70,7 +70,7 @@ export class DarkMatterDimensionState extends DimensionState { } get powerDMPerAscension() { - return POWER_DM_PER_ASCENSION + SingularityMilestone.improvedAscensionDM.effectValue; + return POWER_DM_PER_ASCENSION + SingularityMilestone.improvedAscensionDM.effectOrDefault(0); } get powerDM() { @@ -85,7 +85,7 @@ export class DarkMatterDimensionState extends DimensionState { } get powerDE() { - if (!this.isUnlocked) return 0; + if (!this.isUnlocked || Pelle.isDoomed) return 0; const tierFactor = Math.pow(15, this.tier - 1); const destabilizeBoost = Laitela.isFullyDestabilized ? 8 : 1; return new Decimal(((1 + this.data.powerDEUpgrades * 0.1) * @@ -102,14 +102,14 @@ export class DarkMatterDimensionState extends DimensionState { get intervalAfterAscension() { const purchases = Decimal.affordGeometricSeries(Currency.darkMatter.value, this.rawIntervalCost, this.intervalCostIncrease, 0).toNumber(); - return Math.clampMin(this.intervalPurchaseCap, SingularityMilestone.ascensionIntervalScaling.effectValue * + return Math.clampMin(this.intervalPurchaseCap, SingularityMilestone.ascensionIntervalScaling.effectOrDefault(1200) * this.rawInterval * Math.pow(INTERVAL_PER_UPGRADE, purchases)); } get adjustedStartingCost() { const tiers = [null, 0, 2, 5, 13]; return 10 * Math.pow(COST_MULT_PER_TIER, tiers[this.tier]) * - SingularityMilestone.darkDimensionCostReduction.effectValue; + SingularityMilestone.darkDimensionCostReduction.effectOrDefault(1); } get rawIntervalCost() { @@ -122,7 +122,7 @@ export class DarkMatterDimensionState extends DimensionState { } get intervalCostIncrease() { - return Math.pow(INTERVAL_COST_MULT, SingularityMilestone.intervalCostScalingReduction.effectValue); + return Math.pow(INTERVAL_COST_MULT, SingularityMilestone.intervalCostScalingReduction.effectOrDefault(1)); } get rawPowerDMCost() { @@ -263,7 +263,7 @@ export const DarkMatterDimensions = { dim.timeSinceLastUpdate -= dim.interval * ticks; } } - if (SingularityMilestone.dim4Generation.isUnlocked && Laitela.annihilationUnlocked) { + if (SingularityMilestone.dim4Generation.canBeApplied && Laitela.annihilationUnlocked) { DarkMatterDimension(4).amount = DarkMatterDimension(4).amount .plus(SingularityMilestone.dim4Generation.effectValue * realDiff / 1000); } diff --git a/javascripts/core/celestials/laitela/laitela.js b/javascripts/core/celestials/laitela/laitela.js index 271e1dd5e..afaa9f005 100644 --- a/javascripts/core/celestials/laitela/laitela.js +++ b/javascripts/core/celestials/laitela/laitela.js @@ -1,9 +1,11 @@ -import { CelestialQuotes } from "../quotes.js"; -import { DC } from "../../constants.js"; -import { DarkMatterDimensions } from "./dark-matter-dimension.js"; +import { DC } from "../../constants"; +import { Quotes } from "../quotes"; + +import { DarkMatterDimensions } from "./dark-matter-dimension"; export const Laitela = { displayName: "Lai'tela", + possessiveName: "Lai'tela's", get celestial() { return player.celestials.laitela; }, @@ -44,7 +46,7 @@ export const Laitela = { }, get matterExtraPurchaseFactor() { return (1 + 0.5 * Math.pow(Decimal.pLog10(Currency.darkMatter.max) / 50, 0.4) * - (1 + SingularityMilestone.continuumMult.effectValue)); + (1 + SingularityMilestone.continuumMult.effectOrDefault(0))); }, get realityReward() { return Math.clampMin(Math.pow(100, this.difficultyTier) * @@ -52,7 +54,7 @@ export const Laitela = { }, // Note that entropy goes from 0 to 1, with 1 being completion get entropyGainPerSecond() { - return Math.clamp(Math.pow(Currency.antimatter.value.log10() / 1e11, 2), 0, 100) / 200; + return Math.clamp(Math.pow(Currency.antimatter.value.add(1).log10() / 1e11, 2), 0, 100) / 200; }, get darkMatterMultGain() { return Decimal.pow(Currency.darkMatter.value.dividedBy(this.annihilationDMRequirement) @@ -78,7 +80,7 @@ export const Laitela = { this.celestial.darkMatterMult += this.darkMatterMultGain; DarkMatterDimensions.reset(); Currency.darkEnergy.reset(); - Laitela.quotes.show(Laitela.quotes.ANNIHILATION); + Laitela.quotes.annihilation.show(); Achievement(176).unlock(); return true; }, @@ -119,135 +121,10 @@ export const Laitela = { this.celestial.difficultyTier = 0; this.celestial.singularityCapIncreases = 0; }, - quotes: new CelestialQuotes("laitela", { - UNLOCK: { - id: 1, - lines: [ - "You finally reached me.", - "I guess it is time to reveal to you,", - "The secrets hidden beneath existence.", - "The omnipresent ruling perfection. Continuum.", - "And the binding keys to the multiverse,", - "Dark Matter and Dark Energy.", - "My knowledge is endless and my wisdom divine.", - "So you can play around all you want.", - "I am Lai'tela, the Celestial of Dimensions,", - "And I will be watching you forever.", - ] - }, - FIRST_DESTABILIZE: { - id: 2, - destabilize: 1, - lines: [ - "It is fine. Unlike the others, I never had a Reality.", - "I built this one just now, precisely so it would collapse.", - "I can rebuild this Reality over and over, unlike them.", - "I could trap all of them if I wanted.", - "You will never find a way to overpower me.", - ] - }, - FIRST_SINGULARITY: { - id: 3, - singularities: 1, - lines: [ - "It is weird, how all beings question things.", - "You are different. You can build and manipulate Dimensions.", - "Were you truly once one of them?", - "You have taken control of the darkness so quickly.", - "Molded them into Dimensions and Points just like one of us.", - "What... ARE you?", - ] - }, - // Note: This happens around e10-e11 singularities - ANNIHILATION: { - id: 4, - lines: [ - "Back to square one.", - "We, the Celestials transcend time and existence.", - "We always know that whatever is lost always comes back eventually.", - "Even if we were to cease, we would just come back stronger.", - "The cycle... repeats forever.", - "Do they also understand? Or was it only you as well?", - "I feel like I should know the answer...", - ] - }, - HALF_DIMENSIONS: { - id: 5, - destabilize: 4, - lines: [ - "You seem to be having too much fun.", - "Just like they did before meeting their... fate.", - "You freed them of their eternal imprisonment, yes?", - "I always regret how harsh I was that day.", - "Maybe it doesn't matter.", - "But I digress. Let's keep constricting this Reality.", - ] - }, - SINGULARITY_1: { - id: 6, - singularities: 1e8, - lines: [ - "What was it again...? Antimatter?", - "That was the first thing you turned into Dimensions?", - "It could not have been an accident.", - "How did you... attain the power to control it?", - "This never happened in all of existence... or did it?", - "My endless knowledge... is it waning?", - ] - }, - SINGULARITY_2: { - id: 7, - singularities: 1e16, - lines: [ - "Of those who tried to control dimensions...", - "Who were they? I cannot seem to remember...", - "And how... did they vanish?", - "Are they... us? Simply transcending existence?", - "Did they surpass us and become something we can't comprehend?", - "Are we all imprisoned in this falsity...", - ] - }, - SINGULARITY_3: { - id: 8, - singularities: 1e24, - lines: [ - "Is this a cycle?", - "Will our existence just end and start anew...", - "Just like... the Dimensions I rule?", - "And if such... what will bring our end?", - "I knew the answer to all these questions...", - "But I forgot all of them...", - "Your power... is it... erasing mine...?", - ] - }, - SINGULARITY_4: { - id: 9, - singularities: 1e32, - lines: [ - "I don't know for how much... longer I can hold.", - "There is... next to nothing left...", - "You have attained... complete and total mastery... over the dark...", - "While I can barely... hold onto my name anymore...", - "What am I meant to be doing anyways?", - "Did... my mistakes cause all of this?", - ] - }, - FULL_DESTABILIZE: { - id: 10, - destabilize: 8, - lines: [ - "I feel... like I had something to say...", - "Who am I? I am not sure...", - "I cannot... hold onto the darkness any longer...", - "I... have nothing left...", - "Something about... destabilizing... collapsing...", - "The end...", - ] - }, - }), + quotes: Quotes.laitela, symbol: "ᛝ" }; EventHub.logic.on(GAME_EVENT.TAB_CHANGED, () => { - if (Tab.celestials.laitela.isOpen) Laitela.quotes.show(Laitela.quotes.UNLOCK); + if (Tab.celestials.laitela.isOpen) Laitela.quotes.unlock.show(); }); diff --git a/javascripts/core/celestials/laitela/singularity.js b/javascripts/core/celestials/laitela/singularity.js index eb6c7443b..d31b59af8 100644 --- a/javascripts/core/celestials/laitela/singularity.js +++ b/javascripts/core/celestials/laitela/singularity.js @@ -1,4 +1,5 @@ -import { GameMechanicState } from "../../game-mechanics/index.js"; +import { GameMechanicState } from "../../game-mechanics/index"; + import { deepmergeAll } from "@/utility/deepmerge"; class SingularityMilestoneState extends GameMechanicState { @@ -30,14 +31,18 @@ class SingularityMilestoneState extends GameMechanicState { return Currency.singularities.gte(this.start); } + get increaseThreshold() { + return this.config.increaseThreshold; + } + nerfCompletions(completions) { - const softcap = this.config.increaseThreshold; + const softcap = this.increaseThreshold; if (!softcap || (completions < softcap)) return completions; return softcap + (completions - softcap) / 3; } unnerfCompletions(completions) { - const softcap = this.config.increaseThreshold; + const softcap = this.increaseThreshold; if (!softcap || (completions < softcap)) return completions; return softcap + (completions - softcap) * 3; } @@ -94,42 +99,13 @@ class SingularityMilestoneState extends GameMechanicState { } } -export const SingularityMilestone = (function() { - const db = GameDatabase.celestials.singularityMilestones; - return { - continuumMult: new SingularityMilestoneState(db.continuumMult), - darkMatterMult: new SingularityMilestoneState(db.darkMatterMult), - darkEnergyMult: new SingularityMilestoneState(db.darkEnergyMult), - darkDimensionCostReduction: new SingularityMilestoneState(db.darkDimensionCostReduction), - singularityMult: new SingularityMilestoneState(db.singularityMult), - darkDimensionIntervalReduction: new SingularityMilestoneState(db.darkDimensionIntervalReduction), - ascensionIntervalScaling: new SingularityMilestoneState(db.ascensionIntervalScaling), - autoCondense: new SingularityMilestoneState(db.autoCondense), - darkDimensionAutobuyers: new SingularityMilestoneState(db.darkDimensionAutobuyers), - darkAutobuyerSpeed: new SingularityMilestoneState(db.darkAutobuyerSpeed), - improvedSingularityCap: new SingularityMilestoneState(db.improvedSingularityCap), - darkFromTesseracts: new SingularityMilestoneState(db.darkFromTesseracts), - dilatedTimeFromSingularities: new SingularityMilestoneState(db.dilatedTimeFromSingularities), - darkFromGlyphLevel: new SingularityMilestoneState(db.darkFromGlyphLevel), - gamespeedFromSingularities: new SingularityMilestoneState(db.gamespeedFromSingularities), - darkFromTheorems: new SingularityMilestoneState(db.darkFromTheorems), - dim4Generation: new SingularityMilestoneState(db.dim4Generation), - darkFromDM4: new SingularityMilestoneState(db.darkFromDM4), - theoremPowerFromSingularities: new SingularityMilestoneState(db.theoremPowerFromSingularities), - darkFromGamespeed: new SingularityMilestoneState(db.darkFromGamespeed), - glyphLevelFromSingularities: new SingularityMilestoneState(db.glyphLevelFromSingularities), - darkFromDilatedTime: new SingularityMilestoneState(db.darkFromDilatedTime), - tesseractMultFromSingularities: new SingularityMilestoneState(db.tesseractMultFromSingularities), - improvedAscensionDM: new SingularityMilestoneState(db.improvedAscensionDM), - realityDEMultiplier: new SingularityMilestoneState(db.realityDEMultiplier), - intervalCostScalingReduction: new SingularityMilestoneState(db.intervalCostScalingReduction), - multFromInfinitied: new SingularityMilestoneState(db.multFromInfinitied), - infinitiedPow: new SingularityMilestoneState(db.infinitiedPow), - }; -}()); +export const SingularityMilestone = mapGameDataToObject( + GameDatabase.celestials.singularityMilestones, + config => new SingularityMilestoneState(config) +); export const SingularityMilestones = { - all: Object.values(SingularityMilestone), + all: SingularityMilestone.all, lastNotified: player.celestials.laitela.lastCheckedMilestones, get sorted() { @@ -227,7 +203,7 @@ export const SingularityMilestones = { // Sorted list of all the values where a singularity milestone exists, used for "new milestone" styling const SingularityMilestoneThresholds = (function() { - return Object.values(GameDatabase.celestials.singularityMilestones) + return SingularityMilestones.all .map(m => Array.range(0, Math.min(50, m.limit)) .filter(r => !m.increaseThreshold || r <= m.increaseThreshold || (r > m.increaseThreshold && ((r - m.increaseThreshold) % 3) === 2)) @@ -243,7 +219,7 @@ export const Singularity = { }, get gainPerCapIncrease() { - return SingularityMilestone.improvedSingularityCap.effectValue; + return SingularityMilestone.improvedSingularityCap.effectOrDefault(11); }, get singularitiesGained() { @@ -264,7 +240,7 @@ export const Singularity = { // Total additional time auto-condense will wait after reaching the condensing requirement get timeDelayFromAuto() { - return this.timePerCondense * (SingularityMilestone.autoCondense.effectValue - 1); + return this.timePerCondense * (SingularityMilestone.autoCondense.effectOrDefault(Infinity) - 1); }, get capIsReached() { @@ -282,16 +258,16 @@ export const Singularity = { }, perform() { - if (!this.capIsReached) return; + if (!this.capIsReached || Pelle.isDoomed) return; EventHub.dispatch(GAME_EVENT.SINGULARITY_RESET_BEFORE); Currency.darkEnergy.reset(); Currency.singularities.add(this.singularitiesGained); - for (const quote of Object.values(Laitela.quotes)) { - if (Currency.singularities.value >= quote.singularities) { - Laitela.quotes.show(quote); + for (const quote of Laitela.quotes.all) { + if (quote.requirement) { + quote.show(); } } @@ -307,5 +283,4 @@ EventHub.logic.on(GAME_EVENT.SINGULARITY_RESET_AFTER, () => { if (newMilestones === 1) GameUI.notify.blackHole(`You reached a Singularity milestone!`); else GameUI.notify.blackHole(`You reached ${formatInt(newMilestones)} Singularity milestones!`); SingularityMilestones.lastNotified = Currency.singularities.value; - if (SingularityMilestones.all.every(completions => completions > 0)) Achievement(177).unlock(); }); diff --git a/javascripts/core/celestials/pelle/galaxy-generator.js b/javascripts/core/celestials/pelle/galaxy-generator.js index 5b4b62130..cf4ad4cd7 100644 --- a/javascripts/core/celestials/pelle/galaxy-generator.js +++ b/javascripts/core/celestials/pelle/galaxy-generator.js @@ -1,5 +1,5 @@ -import { DC } from "../../constants"; import { RebuyableMechanicState } from "../../game-mechanics/rebuyable"; + import { PelleRifts } from "./rifts"; export const GalaxyGenerator = { @@ -23,10 +23,7 @@ export const GalaxyGenerator = { get gainPerSecond() { if (!Pelle.hasGalaxyGenerator) return 0; - // Pretend it's here to avoid softlocks and not because the bottom code returns 1 when you don't have this upg - if (!GalaxyGeneratorUpgrades.additive.canBeApplied) return 0.1; - return DC.D1.timesEffectsOf( - GalaxyGeneratorUpgrades.additive, + return new Decimal(GalaxyGeneratorUpgrades.additive.effectValue).timesEffectsOf( GalaxyGeneratorUpgrades.multiplicative, GalaxyGeneratorUpgrades.antimatterMult, GalaxyGeneratorUpgrades.IPMult, @@ -61,18 +58,35 @@ export const GalaxyGenerator = { loop(diff) { if (this.isCapped) { - Pelle.quotes.show(Pelle.quotes.GALAXY_GENERATOR_RIFTS); + Pelle.quotes.galaxyGeneratorRifts.show(); } if (this.sacrificeActive) { this.capRift.reducedTo = Math.max(this.capRift.reducedTo - 0.03 * diff / 1000, 0); if (this.capRift.reducedTo === 0) { player.celestials.pelle.galaxyGenerator.sacrificeActive = false; player.celestials.pelle.galaxyGenerator.phase++; + + const phase = player.celestials.pelle.galaxyGenerator.phase; + if (phase === 1) { + Pelle.quotes.galaxyGeneratorPhase1.show(); + } else if (phase === 4) { + Pelle.quotes.galaxyGeneratorPhase4.show(); + } + if (!this.capObj) { - Pelle.quotes.show(Pelle.quotes.END); + Pelle.quotes.end.show(); } } PelleRifts.all.forEach(x => x.checkMilestoneStates()); + + // Force-unequip glyphs when the player loses the respective milestone. We call the respec option as normally + // except for one particular case - when we want to respec into protected slots but have no room to do so. In + // that case, we force-respec into the inventory instead + if (!PelleRifts.vacuum.milestones[0].canBeApplied && Glyphs.active.filter(g => g).length > 0) { + Glyphs.unequipAll(player.options.respecIntoProtected && Glyphs.findFreeIndex(true) === -1); + Glyphs.refreshActive(); + } + } player.celestials.pelle.galaxyGenerator.generatedGalaxies += this.gainPerSecond * diff / 1000; player.celestials.pelle.galaxyGenerator.generatedGalaxies = Math.min( @@ -106,9 +120,7 @@ export class GalaxyGeneratorUpgrade extends RebuyableMechanicState { } } -export const GalaxyGeneratorUpgrades = (function() { - return mapGameDataToObject( - GameDatabase.celestials.pelle.galaxyGeneratorUpgrades, - config => new GalaxyGeneratorUpgrade(config) - ); -}()); +export const GalaxyGeneratorUpgrades = mapGameDataToObject( + GameDatabase.celestials.pelle.galaxyGeneratorUpgrades, + config => new GalaxyGeneratorUpgrade(config) +); diff --git a/javascripts/core/celestials/pelle/game-end.js b/javascripts/core/celestials/pelle/game-end.js new file mode 100644 index 000000000..2e13a7fa9 --- /dev/null +++ b/javascripts/core/celestials/pelle/game-end.js @@ -0,0 +1,48 @@ +export const END_STATE_MARKERS = { + // Tab zalgoification starts as soon as endState > 0 + GAME_END: 1, + TAB_START_HIDE: 1.5, + INTERACTIVITY_DISABLED: 2.5, + FADE_AWAY: 2.5, + SAVE_DISABLED: 4, + END_NUMBERS: 4.2, + CREDITS_START: 4.5, + SHOW_NEW_GAME: 13, + SPECTATE_GAME: 13.5, + CREDITS_END: 14.5, +}; + +export const GameEnd = { + get endState() { + if (this.removeAdditionalEnd) return this.additionalEnd; + return Math.max((Math.log10(player.celestials.pelle.records.totalAntimatter.plus(1).log10() + 1) - 8.7) / + (Math.log10(9e15) - 8.7) + this.additionalEnd, 0); + }, + + _additionalEnd: 0, + get additionalEnd() { + return (player.isGameEnd || this.removeAdditionalEnd) ? this._additionalEnd : 0; + }, + set additionalEnd(x) { + this._additionalEnd = (player.isGameEnd || this.removeAdditionalEnd) ? x : 0; + }, + + removeAdditionalEnd: false, + + creditsClosed: false, + creditsEverClosed: false, + + gameLoop(diff) { + if (this.removeAdditionalEnd) { + this.additionalEnd -= Math.min(diff / 200, 0.5); + if (this.additionalEnd < 4) { + this.additionalEnd = 0; + this.removeAdditionalEnd = false; + } + } + if (this.endState >= END_STATE_MARKERS.GAME_END && ui.$viewModel.modal.progressBar === undefined) { + player.isGameEnd = true; + this.additionalEnd += Math.min(diff / 1000 / 20, 0.1); + } + } +}; diff --git a/javascripts/core/celestials/pelle/pelle.js b/javascripts/core/celestials/pelle/pelle.js index 8861bcbce..8d6334172 100644 --- a/javascripts/core/celestials/pelle/pelle.js +++ b/javascripts/core/celestials/pelle/pelle.js @@ -1,9 +1,14 @@ -import { DC } from "../../constants"; import { Currency } from "../../currency"; +import { DC } from "../../constants"; import { RebuyableMechanicState } from "../../game-mechanics/rebuyable"; import { SetPurchasableMechanicState } from "../../utils"; + +import { Quotes } from "../quotes"; + +import wordShift from "../../wordShift"; + import zalgo from "./zalgo"; -import { CelestialQuotes } from "../quotes.js"; + const disabledMechanicUnlocks = { achievements: () => ({}), @@ -19,7 +24,7 @@ const disabledMechanicUnlocks = { autoec: () => ({}), replicantiIntervalMult: () => ({}), tpMults: () => ({}), - glyphs: () => !PelleRifts.famine.milestones[0].canBeApplied, + glyphs: () => !PelleRifts.vacuum.milestones[0].canBeApplied, V: () => ({}), singularity: () => ({}), continuum: () => ({}), @@ -50,17 +55,11 @@ const disabledMechanicUnlocks = { export const Pelle = { symbol: "♅", + // Suppress the randomness for this form + possessiveName: "Pelle's", get displayName() { - return Date.now() % 4000 > 500 ? "Pelle" : Pelle.modalTools.randomCrossWords("Pelle"); - }, - - additionalEnd: 0, - addAdditionalEnd: true, - - get endState() { - return Math.max((Math.log10(player.celestials.pelle.records.totalAntimatter.plus(1).log10() + 1) - 8.7) / - (Math.log10(9e15) - 8.7) + this.additionalEnd, 0); + return Date.now() % 4000 > 500 ? "Pelle" : wordShift.randomCrossWords("Pelle"); }, get isUnlocked() { @@ -99,16 +98,16 @@ export const Pelle = { } finishProcessReality({ reset: true, armageddon: true }); disChargeAll(); - this.cel.armageddonDuration = 0; + player.celestials.enslaved.isStoringReal = false; + player.celestials.enslaved.autoStoreReal = false; + if (PelleStrikes.dilation.hasStrike) player.dilation.active = true; EventHub.dispatch(GAME_EVENT.ARMAGEDDON_AFTER, gainStuff); }, gameLoop(diff) { if (this.isDoomed) { - this.cel.armageddonDuration += diff; Currency.realityShards.add(this.realityShardGainPerSecond.times(diff).div(1000)); PelleRifts.all.forEach(r => r.fill(diff)); - if (this.endState >= 1 && Pelle.addAdditionalEnd) this.additionalEnd += Math.min(diff / 1000 / 20, 0.1); } }, @@ -120,12 +119,9 @@ export const Pelle = { return this.cel.doomed; }, - get currentArmageddonDuration() { - return this.cel.armageddonDuration; - }, - get disabledAchievements() { - return [143, 142, 141, 125, 118, 117, 111, 104, 103, 92, 91, 78, 76, 74, 65, 55, 54, 37]; + return [164, 143, 142, 141, 137, 134, 133, 132, 125, 118, 117, 111, 104, 103, 93, 92, 91, 87, 85, 78, 76, + 74, 65, 55, 54, 37]; }, get uselessInfinityUpgrades() { @@ -133,9 +129,7 @@ export const Pelle = { }, get uselessTimeStudies() { - const uselessTimeStudies = [32, 41, 51, 61, 62, 121, 122, 123, 141, 142, 143, 192, 213]; - if (PelleUpgrade.replicantiGalaxyNoReset.canBeApplied) uselessTimeStudies.push(33); - return uselessTimeStudies; + return [32, 41, 51, 61, 62, 121, 122, 123, 141, 142, 143, 192, 213]; }, get disabledRUPGs() { @@ -144,59 +138,24 @@ export const Pelle = { get uselessPerks() { return [10, 12, 13, 14, 15, 16, 17, 30, 40, 41, 42, 43, 44, 45, 46, 51, 53, - 60, 61, 62, 80, 81, 82, 83, 100, 105, 106]; - }, - - // Glyph effects are controlled through other means, but are also enumerated here for accessing to improve UX. Note - // that this field is NEGATED, describing an effect allowlist instead of a blocklist, as most of the effects are - // already disabled by virtue of the glyph type being unequippable and many of the remaining ones are also disabled. - get enabledGlyphEffects() { - return ["timepow", "timespeed", "timeshardpow", - "dilationpow", "dilationgalaxyThreshold", - "replicationpow", - "powerpow", "powermult", "powerdimboost", "powerbuy10", - "infinitypow", "infinityrate", - "companiondescription", "companionEP"]; + 60, 61, 62, 80, 81, 82, 83, 100, 105, 106, 201, 202, 203, 204]; }, get specialGlyphEffect() { const isUnlocked = this.isDoomed && PelleRifts.chaos.milestones[1].canBeApplied; - let description; - switch (Pelle.activeGlyphType) { - case "infinity": - description = "Infinity Point gain {value} (based on current IP)"; - break; - case "time": - description = "Eternity Point gain {value} (based on current EP)"; - break; - case "replication": - description = "Replication speed {value} (based on Famine)"; - break; - case "dilation": - description = "Dilated Time gain {value} (based on Tachyon Galaxies)"; - break; - case "power": - description = `Galaxies are ${formatPercents(0.02)} stronger`; - break; - case "companion": - description = `You feel ${formatPercents(0.34)} better`; - break; - default: - description = "No glyph equipped!"; - break; - } + const description = this.getSpecialGlyphEffectDescription(this.activeGlyphType); const isActive = type => isUnlocked && this.activeGlyphType === type; return { isUnlocked, description, infinity: (isActive("infinity") && player.challenge.eternity.current <= 8) - ? Currency.infinityPoints.value.pow(0.2) + ? Currency.infinityPoints.value.plus(1).pow(0.2) : DC.D1, time: isActive("time") ? Currency.eternityPoints.value.plus(1).pow(0.3) : DC.D1, replication: isActive("replication") - ? 10 ** 53 ** (PelleRifts.famine.percentage) + ? 10 ** 53 ** (PelleRifts.vacuum.percentage) : 1, dilation: isActive("dilation") ? Decimal.pow(player.dilation.totalTachyonGalaxies, 1.5).max(1) @@ -210,9 +169,32 @@ export const Pelle = { isScaling: () => ["infinity", "time", "replication", "dilation"].includes(this.activeGlyphType), }; }, - - get uselessRaMilestones() { - return [0, 1, 15, 18, 19, 21]; + getSpecialGlyphEffectDescription(type) { + switch (type) { + case "infinity": + return `Infinity Point gain ${player.challenge.eternity.current <= 8 + ? formatX(Currency.infinityPoints.value.plus(1).pow(0.2), 2) + : formatX(DC.D1, 2)} (based on current IP)`; + case "time": + return `Eternity Point gain ${formatX(Currency.eternityPoints.value.plus(1).pow(0.3), 2)} + (based on current EP)`; + case "replication": + return `Replication speed ${formatX(10 ** 53 ** (PelleRifts.vacuum.percentage), 2)} \ + (based on ${wordShift.wordCycle(PelleRifts.vacuum.name)})`; + case "dilation": + return `Dilated Time gain ${formatX(Decimal.pow(player.dilation.totalTachyonGalaxies, 1.5).max(1), 2)} + (based on Tachyon Galaxies)`; + case "power": + return `Galaxies are ${formatPercents(0.02)} stronger`; + case "companion": + return `You feel ${formatPercents(0.34)} better`; + // Undefined means that there is no glyph equipped, needs to be here since this function is used in + // both Current Glyph Effects and Glyph Tooltip + case undefined: + return "No Glyph equipped!"; + default: + return ""; + } }, get remnantRequirementForDilation() { @@ -281,7 +263,7 @@ export const Pelle = { // Transition text from "from" to "to", stage is 0-1, 0 is fully "from" and 1 is fully "to" // Also adds more zalgo the bigger the stage transitionText(from, to, stage = 0) { - const len = (from.length * (1 - stage) + to.length * stage); + const len = Math.round((from.length * (1 - stage) + to.length * stage) * 1e8) / 1e8; const toInterval = len * (1 - stage); let req = toInterval; let str = ""; @@ -298,117 +280,31 @@ export const Pelle = { return zalgo(str, Math.floor(stage ** 2 * 7)); }, - endTabNames: "End Is Nigh Destruction Is Imminent Help Us Good Bye".split(" "), + endTabNames: "End Is Nigh Destruction Is Imminent Help Us Good Bye Forever".split(" "), - modalTools: { - bracketOrder: ["()", "[]", "{}", "<>", "||"], - wordCycle(x) { - const list = x.split("-"); - const len = list.length; - const maxWordLen = list.reduce((acc, str) => Math.max(acc, str.length), 0); - const tick = Math.floor(Date.now() / 200) % (len * 5); - const largeTick = Math.floor(tick / 5); - const bP = this.bracketOrder[largeTick]; - let v = list[largeTick]; - if (tick % 5 < 1 || tick % 5 > 3) { - v = this.randomCrossWords(v); - } - // Stands for Bracket Pair. - const space = (maxWordLen - v.length) / 2; - return bP[0] + ".".repeat(Math.floor(space)) + v + ".".repeat(Math.ceil(space)) + bP[1]; - }, - randomCrossWords(str) { - const x = str.split(""); - for (let i = 0; i < x.length / 1.7; i++) { - const randomIndex = Math.floor(this.predictableRandom(Math.floor(Date.now() / 500) % 964372 + i) * x.length); - // .splice should return the deleted index. - x[randomIndex] = this.randomSymbol; - } - return x.join(""); - }, - predictableRandom(x) { - let start = Math.pow(x % 97, 4.3) * 232344573; - const a = 15485863; - const b = 521791; - start = (start * a) % b; - for (let i = 0; i < (x * x) % 90 + 90; i++) { - start = (start * a) % b; - } - return start / b; - }, - celCycle(x) { - // Gets trailing number and removes it - const cels = x.split("-").map(cel => [parseInt(cel, 10), cel.replace(/\d+/u, "")]); - const totalTime = cels.reduce((acc, cel) => acc + cel[0], 0); - let tick = (Date.now() / 100) % totalTime; - let index = -1; - while (tick >= 0 && index < cels.length - 1) { - index++; - tick -= cels[index][0]; - } - return ``; - }, - get randomSymbol() { - return String.fromCharCode(Math.floor(Math.random() * 50) + 192); - } - }, - quotes: new CelestialQuotes("pelle", (function() { - const wc = function(x) { - return Pelle.modalTools.wordCycle.bind(Pelle.modalTools)(x); - }; - const cc = function(x) { - return Pelle.modalTools.celCycle.bind(Pelle.modalTools)(x); - }; - const p = function(line) { - if (!line.includes("[") && !line.includes("<")) return line; - - const sep = " ---TEMPSEPERATOR--- "; - const ops = []; - for (let i = 0; i < line.length; i++) { - if (line[i] === "[") ops.push(wc); - else if (line[i] === "<") ops.push(cc); - } - let l = line.replace("[", sep).replace("]", sep); - l = l.replace("<", sep).replace(">", sep).split(sep); - return () => l.map((v, x) => ((x % 2) ? ops[x / 2 - 0.5](v) : v)).join(""); - }; - - const quotesObject = {}; - let iterator = 0; - for (const i in GameDatabase.celestials.pelle.quotes) { - iterator++; - quotesObject[i] = { - id: iterator, - lines: GameDatabase.celestials.pelle.quotes[i].map(x => p(x)) - }; - } - return quotesObject; - }())), - hasQuote(x) { - return player.celestials.pelle.quotes.includes(x); - }, + quotes: Quotes.pelle, }; EventHub.logic.on(GAME_EVENT.ARMAGEDDON_AFTER, () => { if (Currency.remnants.gte(1)) { - Pelle.quotes.show(Pelle.quotes.ARM); + Pelle.quotes.arm.show(); } }); EventHub.logic.on(GAME_EVENT.PELLE_STRIKE_UNLOCKED, () => { if (PelleStrikes.infinity.hasStrike) { - Pelle.quotes.show(Pelle.quotes.STRIKE_1); + Pelle.quotes.strike1.show(); } if (PelleStrikes.powerGalaxies.hasStrike) { - Pelle.quotes.show(Pelle.quotes.STRIKE_2); + Pelle.quotes.strike2.show(); } if (PelleStrikes.eternity.hasStrike) { - Pelle.quotes.show(Pelle.quotes.STRIKE_3); + Pelle.quotes.strike3.show(); } if (PelleStrikes.ECs.hasStrike) { - Pelle.quotes.show(Pelle.quotes.STRIKE_4); + Pelle.quotes.strike4.show(); } if (PelleStrikes.dilation.hasStrike) { - Pelle.quotes.show(Pelle.quotes.STRIKE_5); + Pelle.quotes.strike5.show(); } }); @@ -464,15 +360,13 @@ export class PelleUpgradeState extends SetPurchasableMechanicState { } -export const PelleUpgrade = (function() { - return mapGameDataToObject( - GameDatabase.celestials.pelle.upgrades, - config => (config.rebuyable - ? new RebuyablePelleUpgradeState(config) - : new PelleUpgradeState(config) - ) - ); -}()); +export const PelleUpgrade = mapGameDataToObject( + GameDatabase.celestials.pelle.upgrades, + config => (config.rebuyable + ? new RebuyablePelleUpgradeState(config) + : new PelleUpgradeState(config) + ) +); PelleUpgrade.rebuyables = PelleUpgrade.all.filter(u => u.isRebuyable); PelleUpgrade.singles = PelleUpgrade.all.filter(u => !u.isRebuyable); diff --git a/javascripts/core/celestials/pelle/rifts.js b/javascripts/core/celestials/pelle/rifts.js index 3682e67fd..59c6020cc 100644 --- a/javascripts/core/celestials/pelle/rifts.js +++ b/javascripts/core/celestials/pelle/rifts.js @@ -20,7 +20,7 @@ class RiftMilestoneState extends GameMechanicState { } get isUnlocked() { - if (this.resource === "pestilence" && PelleRifts.chaos.milestones[0].isEffectActive) return true; + if (this.resource === "decay" && PelleRifts.chaos.milestones[0].isEffectActive) return true; return this.requirement <= PelleRifts[this.resource].percentage; } @@ -131,6 +131,10 @@ class RiftState extends GameMechanicState { return this.percentage >= 1; } + get galaxyGeneratorText() { + return this.config.galaxyGeneratorText; + } + toggle() { const active = PelleRifts.all.filter(r => r.isActive).length; if (!this.isActive && active === 2) GameUI.notify.error(`You can only have 2 rifts active at the same time!`); @@ -165,16 +169,14 @@ class RiftState extends GameMechanicState { this.fillCurrency.value = Math.max(this.fillCurrency.value - spent, 0); this.totalFill = Math.clampMax(this.totalFill + spent, this.maxValue); } - if (PelleRifts.famine.milestones[0].canBeApplied) Glyphs.refreshActive(); + if (PelleRifts.vacuum.milestones[0].canBeApplied) Glyphs.refreshActive(); this.checkMilestoneStates(); } } -export const PelleRifts = (function() { - return mapGameDataToObject( - GameDatabase.celestials.pelle.rifts, - config => new RiftState(config) - ); -}()); +export const PelleRifts = mapGameDataToObject( + GameDatabase.celestials.pelle.rifts, + config => new RiftState(config) +); PelleRifts.totalMilestones = () => PelleRifts.all.flatMap(x => x.milestones).countWhere(x => x.canBeApplied); diff --git a/javascripts/core/celestials/pelle/strikes.js b/javascripts/core/celestials/pelle/strikes.js index e2d6d438b..85aa4fc11 100644 --- a/javascripts/core/celestials/pelle/strikes.js +++ b/javascripts/core/celestials/pelle/strikes.js @@ -1,15 +1,15 @@ -import { GameMechanicState } from "../../utils"; +import { BitUpgradeState } from "../../utils"; -// TODO: BitUpgradeState? wrapper for this + effarig + enslaved -class PelleStrikeState extends GameMechanicState { - constructor(config) { - super(config); - if (this.id < 0 || this.id > 31) throw new Error(`Id ${this.id} out of bit range`); - } +class PelleStrikeState extends BitUpgradeState { + get bits() { return player.celestials.pelle.progressBits; } + set bits(value) { player.celestials.pelle.progressBits = value; } get hasStrike() { - // eslint-disable-next-line no-bitwise - return Boolean(player.celestials.pelle.progressBits & (1 << this.id)); + return this.isUnlocked; + } + + get canBeUnlocked() { + return Pelle.isDoomed && !this.hasStrike; } get requirement() { @@ -22,8 +22,8 @@ class PelleStrikeState extends GameMechanicState { return typeof x === "function" ? x() : x; } - get reward() { - return this.config.rewardDescription; + reward() { + return this.config.rewardDescription(); } get rift() { @@ -31,10 +31,14 @@ class PelleStrikeState extends GameMechanicState { } trigger() { - if (!Pelle.isDoomed || this.hasStrike) return; - this.unlockStrike(); + this.unlock(); + } - // If it's death, reset the records + onUnlock() { + GameUI.notify.strike(`You encountered a Pelle Strike: ${this.requirement}`); + player.celestials.pelle.collapsed.rifts = false; + + // If it's paradox, reset the records if (this.id === 5) { Pelle.cel.records.totalAntimatter = new Decimal("1e180000"); Pelle.cel.records.totalInfinityPoints = new Decimal("1e60000"); @@ -46,21 +50,12 @@ class PelleStrikeState extends GameMechanicState { // softlocked, or starting it too late and getting not-softlocked. Pelle.cel.records.totalEternityPoints = new Decimal("1e1050"); } - } - - unlockStrike() { - GameUI.notify.strike(`You encountered a Pelle Strike: ${this.requirement}`); - player.celestials.pelle.collapsed.rifts = false; Tab.celestials.pelle.show(); - // eslint-disable-next-line no-bitwise - player.celestials.pelle.progressBits |= (1 << this.id); EventHub.dispatch(GAME_EVENT.PELLE_STRIKE_UNLOCKED); } } -export const PelleStrikes = (function() { - return mapGameDataToObject( - GameDatabase.celestials.pelle.strikes, - config => new PelleStrikeState(config) - ); -}()); +export const PelleStrikes = mapGameDataToObject( + GameDatabase.celestials.pelle.strikes, + config => new PelleStrikeState(config) +); diff --git a/javascripts/core/celestials/quotes.js b/javascripts/core/celestials/quotes.js index 0450a667a..9555028fd 100644 --- a/javascripts/core/celestials/quotes.js +++ b/javascripts/core/celestials/quotes.js @@ -1,44 +1,178 @@ -export class CelestialQuotes { - constructor(celestialName, quoteData) { - this.quotesById = []; - for (const quoteKey of Object.keys(quoteData)) { - if (this[quoteKey] !== undefined) { - throw new Error(`Celestial quote keys should not replace existing properties (${quoteKey})`); - } - const quote = quoteData[quoteKey]; - this[quoteKey] = quote; - this.quotesById[quote.id] = quote; +import { BitUpgradeState } from "../game-mechanics/index"; +import wordShift from "../wordShift"; + +export const Quote = { + addToQueue(quote) { + ui.view.quotes.queue.push(quote); + if (!ui.view.quotes.current) this.advanceQueue(); + }, + advanceQueue() { + ui.view.quotes.current = ui.view.quotes.queue.shift(); + }, + showHistory(history) { + ui.view.quotes.history = history; + }, + clearQueue() { + ui.view.quotes.queue = []; + ui.view.quotes.current = undefined; + }, + clearHistory() { + ui.view.quotes.history = undefined; + }, + clearAll() { + this.clearQueue(); + this.clearHistory(); + }, + get isOpen() { + return ui.view.quotes.current !== undefined; + }, + get isHistoryOpen() { + return ui.view.quotes.history !== undefined; + } +}; + +// Gives an array specifying proportions of celestials to blend together on the modal, as a function of time, to +// provide a smoother transition between different celestials to reduce potential photosensitivity issues +function blendCel(cels) { + const totalTime = cels.map(cel => cel[1]).sum(); + const tick = (Date.now() / 1000) % totalTime; + + // Blend the first blendTime seconds with the previous celestial and the last blendTime seconds with the next; + // note that this results in a total transition time of 2*blendTime. We specifically set this to be half the duration + // of the first entry - this is because in the case of all intervals having the same duration, this guarantees two + // blended entries at all points in time. + const blendTime = cels[0][1] / 2; + let start = 0; + for (let index = 0; index < cels.length; index++) { + const prevCel = cels[(index + cels.length - 1) % cels.length], currCel = cels[index], + nextCel = cels[(index + 1) % cels.length]; + + // Durations of time from after last transition and after next transition. May be negative, which is how we + // check to see if we're in the correct time interval (last should be positive, next should be negative) + const lastTime = tick - start, nextTime = lastTime - currCel[1]; + if (nextTime > 0) { + start += currCel[1]; + continue; } - this._celestial = celestialName; + + if (lastTime <= blendTime) { + const t = 0.5 * lastTime / blendTime; + return [[prevCel[0], 0.5 - t], [currCel[0], 0.5 + t]]; + } + if (-nextTime <= blendTime) { + const t = 0.5 * nextTime / blendTime; + return [[currCel[0], 0.5 - t], [nextCel[0], 0.5 + t]]; + } + + // In principle the animation properties should never get to this return case, but we leave it here just in case - + // the worst side-effect of reaching here is that some UI elements may appear to lose click detection for a + // fraction of a second when transitioning from two blended entries to one + return [[currCel[0], 1]]; + } + throw new Error("Could not blend celestial fractions in Quote modal"); +} + +class QuoteLine { + constructor(line, parent) { + this._parent = parent; + this._showCelestialName = line.showCelestialName ?? true; + + this._celestialArray = line.background + ? () => blendCel(line.background) + : [[parent.celestial, 1]]; + + const replacementMatch = /\$(\d+)/gu; + + this._line = typeof line === "string" + ? line + // This matches each digit after a $ and replaces it with the wordCycle of an array with the digit it matched. + : () => line.text.replaceAll(replacementMatch, (_, i) => wordShift.wordCycle(line[i])); } - static singleLine(id, line) { - return { - id, - lines: [line] - }; + get line() { + return typeof this._line === "function" ? this._line() : this._line; } - fromID(id) { - return this.quotesById[id]; + get celestials() { + return typeof this._celestialArray === "function" ? this._celestialArray() : this._celestialArray; } - get seenArray() { - return player.celestials[this._celestial].quotes; + get celestialSymbols() { + return this.celestials.map(c => Celestials[c[0]].symbol); } - seen(data) { - return this.seenArray.includes(data.id); + get showCelestialName() { + return this._showCelestialName; } - show(data) { - if (this.seen(data)) return; - this.seenArray.push(data.id); - Modal.celestialQuote.show(this._celestial, data.lines); - } - - forget(data) { - const index = this.seenArray.indexOf(data.id); - if (index >= 0) this.seenArray.splice(index, 1); + get celestialName() { + return Celestials[this._parent.celestial].displayName; } } + +class CelQuotes extends BitUpgradeState { + constructor(config, celestial) { + super(config); + this._celestial = celestial; + this._lines = config.lines.map(line => new QuoteLine(line, this)); + } + + get bits() { return player.celestials[this._celestial].quoteBits; } + set bits(value) { player.celestials[this._celestial].quoteBits = value; } + + get requirement() { + // If requirement is defined, it is always a function returning a boolean. + return this.config.requirement?.(); + } + + get celestial() { + return this._celestial; + } + + line(id) { + return this._lines[id]; + } + + get totalLines() { + return this._lines.length; + } + + show() { this.unlock(); } + onUnlock() { this.present(); } + + present() { + Quote.addToQueue(this); + } +} + + +export const Quotes = { + teresa: mapGameDataToObject( + GameDatabase.celestials.quotes.teresa, + config => new CelQuotes(config, "teresa") + ), + effarig: mapGameDataToObject( + GameDatabase.celestials.quotes.effarig, + config => new CelQuotes(config, "effarig") + ), + enslaved: mapGameDataToObject( + GameDatabase.celestials.quotes.enslaved, + config => new CelQuotes(config, "enslaved") + ), + v: mapGameDataToObject( + GameDatabase.celestials.quotes.v, + config => new CelQuotes(config, "v") + ), + ra: mapGameDataToObject( + GameDatabase.celestials.quotes.ra, + config => new CelQuotes(config, "ra") + ), + laitela: mapGameDataToObject( + GameDatabase.celestials.quotes.laitela, + config => new CelQuotes(config, "laitela") + ), + pelle: mapGameDataToObject( + GameDatabase.celestials.quotes.pelle, + config => new CelQuotes(config, "pelle") + ), +}; diff --git a/javascripts/core/celestials/ra/alchemy.js b/javascripts/core/celestials/ra/alchemy.js index 84e7f6e8f..d9d8c66dc 100644 --- a/javascripts/core/celestials/ra/alchemy.js +++ b/javascripts/core/celestials/ra/alchemy.js @@ -1,4 +1,4 @@ -import { GameMechanicState } from "../../game-mechanics/index.js"; +import { GameMechanicState } from "../../game-mechanics/index"; /** * @abstract @@ -194,7 +194,7 @@ class AlchemyReaction { // Reactions are per-10 products because that avoids decimals in the UI for reagents, but efficiency losses can make // products have decimal coefficients. get baseProduction() { - return this.isReality ? 1 : 5 * Effects.sum(GlyphSacrifice.reality); + return this.isReality ? 1 : 5; } get reactionEfficiency() { @@ -224,44 +224,16 @@ class AlchemyReaction { } } -export const AlchemyResource = (function() { - function createResource(resource) { - const config = GameDatabase.celestials.alchemy.resources[resource]; - config.id = resource; - if (config.isBaseResource) { - return new BasicAlchemyResourceState(config); - } - return new AdvancedAlchemyResourceState(config); - } - - return { - power: createResource(ALCHEMY_RESOURCE.POWER), - infinity: createResource(ALCHEMY_RESOURCE.INFINITY), - time: createResource(ALCHEMY_RESOURCE.TIME), - replication: createResource(ALCHEMY_RESOURCE.REPLICATION), - dilation: createResource(ALCHEMY_RESOURCE.DILATION), - cardinality: createResource(ALCHEMY_RESOURCE.CARDINALITY), - eternity: createResource(ALCHEMY_RESOURCE.ETERNITY), - dimensionality: createResource(ALCHEMY_RESOURCE.DIMENSIONALITY), - inflation: createResource(ALCHEMY_RESOURCE.INFLATION), - alternation: createResource(ALCHEMY_RESOURCE.ALTERNATION), - effarig: createResource(ALCHEMY_RESOURCE.EFFARIG), - synergism: createResource(ALCHEMY_RESOURCE.SYNERGISM), - momentum: createResource(ALCHEMY_RESOURCE.MOMENTUM), - decoherence: createResource(ALCHEMY_RESOURCE.DECOHERENCE), - exponential: createResource(ALCHEMY_RESOURCE.EXPONENTIAL), - force: createResource(ALCHEMY_RESOURCE.FORCE), - uncountability: createResource(ALCHEMY_RESOURCE.UNCOUNTABILITY), - boundless: createResource(ALCHEMY_RESOURCE.BOUNDLESS), - multiversal: createResource(ALCHEMY_RESOURCE.MULTIVERSAL), - unpredictability: createResource(ALCHEMY_RESOURCE.UNPREDICTABILITY), - reality: createResource(ALCHEMY_RESOURCE.REALITY) - }; -}()); +export const AlchemyResource = mapGameDataToObject( + GameDatabase.celestials.alchemy.resources, + config => (config.isBaseResource + ? new BasicAlchemyResourceState(config) + : new AdvancedAlchemyResourceState(config)) +); export const AlchemyResources = { - all: Object.values(AlchemyResource), - base: Object.values(AlchemyResource).filter(r => r.isBaseResource) + all: AlchemyResource.all, + base: AlchemyResource.all.filter(r => r.isBaseResource) }; export const AlchemyReactions = (function() { @@ -269,7 +241,7 @@ export const AlchemyReactions = (function() { function mapReagents(resource) { return resource.config.reagents .map(r => ({ - resource: AlchemyResources.all[r.resource], + resource: AlchemyResources.all.find(x => x.id === r.resource), cost: r.amount })); } diff --git a/javascripts/core/celestials/ra/ra.js b/javascripts/core/celestials/ra/ra.js index df19ed0ba..2f6d944da 100644 --- a/javascripts/core/celestials/ra/ra.js +++ b/javascripts/core/celestials/ra/ra.js @@ -1,5 +1,56 @@ -import { GameMechanicState } from "../../utils.js"; -import { CelestialQuotes } from "../quotes.js"; +import { BitUpgradeState, GameMechanicState } from "../../game-mechanics/index"; +import { Quotes } from "../quotes"; + +class RaUnlockState extends BitUpgradeState { + get bits() { return player.celestials.ra.unlockBits; } + set bits(value) { player.celestials.ra.unlockBits = value; } + + get disabledByPelle() { + return Pelle.isDoomed && this.config.disabledByPelle; + } + + get isEffectActive() { + return this.isUnlocked && !this.disabledByPelle; + } + + get requirementText() { + const pet = this.pet.name; + return this.level === 1 + ? `Unlock ${pet}` + : `Get ${pet} to level ${this.level}`; + } + + get reward() { + return typeof this.config.reward === "function" + ? this.config.reward() + : this.config.reward; + } + + get displayIcon() { + return this.disabledByPelle ? `` : this.config.displayIcon; + } + + get pet() { + return Ra.pets[this.config.pet]; + } + + get level() { + return this.config.level; + } + + get canBeUnlocked() { + return this.pet.level >= this.level && !this.isUnlocked; + } + + onUnlock() { + this.config.onUnlock?.(); + } +} + +const unlocks = mapGameDataToObject( + GameDatabase.celestials.ra.unlocks, + config => new RaUnlockState(config) +); class RaPetState extends GameMechanicState { get data() { @@ -35,7 +86,7 @@ class RaPetState extends GameMechanicState { } get isUnlocked() { - return this.requiredUnlock === undefined || Ra.has(this.requiredUnlock); + return this.requiredUnlock === undefined || this.requiredUnlock.isUnlocked; } get isCapped() { @@ -43,7 +94,7 @@ class RaPetState extends GameMechanicState { } get level() { - return this.data.level; + return this.isUnlocked ? this.data.level : 0; } set level(value) { @@ -71,10 +122,11 @@ class RaPetState extends GameMechanicState { } get memoryChunksPerSecond() { - let res = this.canGetMemoryChunks ? this.rawMemoryChunksPerSecond : 0; - res *= RA_UNLOCKS.TT_BOOST.effect.memoryChunks(); - res *= this.chunkUpgradeCurrentMult; - if (this.hasRecollection) res *= RA_UNLOCKS.RA_RECOLLECTION_UNLOCK.effect; + if (!this.canGetMemoryChunks) return 0; + let res = this.rawMemoryChunksPerSecond * this.chunkUpgradeCurrentMult * + Effects.product(Ra.unlocks.continuousTTBoost.effects.memoryChunks, GlyphSacrifice.reality); + if (this.hasRemembrance) res *= Ra.remembrance.multiplier; + else if (Ra.petWithRemembrance) res *= Ra.remembrance.nerf; return res; } @@ -82,8 +134,8 @@ class RaPetState extends GameMechanicState { return this.isUnlocked && Ra.isRunning; } - get hasRecollection() { - return Ra.petWithRecollection === this.name; + get hasRemembrance() { + return Ra.petWithRemembrance === this.name; } get memoryUpgradeCurrentMult() { @@ -140,6 +192,12 @@ class RaPetState extends GameMechanicState { Ra.checkForUnlocks(); } + get unlocks() { + return Ra.unlocks.all + .filter(x => x.pet === this) + .sort((a, b) => a.level - b.level); + } + tick(realDiff, generateChunks) { const seconds = realDiff / 1000; const newMemoryChunks = generateChunks @@ -162,12 +220,24 @@ class RaPetState extends GameMechanicState { } } +const pets = mapGameDataToObject( + GameDatabase.celestials.ra.pets, + config => new RaPetState(config) +); + export const Ra = { displayName: "Ra", - pets: mapGameDataToObject( - GameDatabase.celestials.ra, - config => new RaPetState(config) - ), + possessiveName: "Ra's", + unlocks, + pets, + remembrance: { + multiplier: 5, + nerf: 0.5, + requiredLevels: 20, + get isUnlocked() { + return Ra.totalPetLevel >= this.requiredLevels; + } + }, // Dev/debug function for easier testing reset() { const data = player.celestials.ra; @@ -190,7 +260,7 @@ export const Ra = { for (const pet of Ra.pets.all) pet.tick(realDiff, generateChunks); }, get productionPerMemoryChunk() { - let res = RA_UNLOCKS.TT_BOOST.effect.memories() * Achievement(168).effectOrDefault(1); + let res = Effects.product(Ra.unlocks.continuousTTBoost.effects.memories, Achievement(168)); for (const pet of Ra.pets.all) { if (pet.isUnlocked) res *= pet.memoryProductionMultiplier; } @@ -201,7 +271,7 @@ export const Ra = { for (const pet of Ra.pets.all) { if (pet.memoryProductionMultiplier !== 1) boostList.push(pet.memoryGain); } - if (Ra.has(RA_UNLOCKS.TT_BOOST)) boostList.push("current Time Theorems"); + if (Ra.unlocks.continuousTTBoost.canBeApplied) boostList.push("current Time Theorems"); if (boostList.length === 1) return `${boostList[0]}`; if (boostList.length === 2) return `${boostList[0]} and ${boostList[1]}`; @@ -231,55 +301,34 @@ export const Ra = { return this.levelCap * this.pets.all.length; }, checkForUnlocks() { - if (!V.has(V_UNLOCKS.RA_UNLOCK)) return; - for (const unl of Object.values(RA_UNLOCKS)) { - const isUnlockable = unl.totalLevels === undefined - ? unl.pet.isUnlocked && unl.pet.level >= unl.level - : this.totalPetLevel >= unl.totalLevels; - if (isUnlockable && !this.has(unl)) { - // eslint-disable-next-line no-bitwise - player.celestials.ra.unlockBits |= (1 << unl.id); - if (unl.id === RA_UNLOCKS.ALWAYS_GAMESPEED.id) { - const allGlyphs = player.reality.glyphs.active - .concat(player.reality.glyphs.inventory); - for (const glyph of allGlyphs) { - Glyphs.applyGamespeed(glyph); - } - } - } + if (!VUnlocks.raUnlock.canBeApplied) return; + for (const unl of Ra.unlocks.all) { + unl.unlock(); } - for (const quote of Object.values(Ra.quotes)) { - // Quotes without requirements will be shown in other ways - need to check if it exists before calling though - if (quote.requirement && quote.requirement()) { - // TODO If multiple quotes show up simultaneously, this only seems to actually show one of them and skips the - // rest. This might be related to the modal stacking issue - Ra.quotes.show(quote); + Ra.checkForQuotes(); + }, + checkForQuotes() { + for (const quote of Ra.quotes.all) { + // Quotes without requirements will be shown in other ways + if (quote.requirement) { + quote.show(); } } }, - has(info) { - // eslint-disable-next-line no-bitwise - return Boolean(player.celestials.ra.unlockBits & (1 << info.id)); - }, initializeRun() { clearCelestialRuns(); player.celestials.ra.run = true; - this.quotes.show(this.quotes.REALITY_ENTER); + this.quotes.realityEnter.show(); }, toggleMode() { player.celestials.ra.activeMode = !player.celestials.ra.activeMode; }, - gamespeedDTMult() { - if (!Ra.has(RA_UNLOCKS.PEAK_GAMESPEED)) return 1; - return Math.max(Math.pow(Math.log10(player.celestials.ra.peakGamespeed) - 90, 3), 1); - }, // This gets widely used in lots of places since the relevant upgrade is "all forms of continuous non-dimension // production", which in this case is infinities, eternities, replicanti, dilated time, and time theorem generation. // It also includes the 1% IP time study, Teresa's 1% EP upgrade, and the charged RM generation upgrade. Note that // removing the hardcap of 10 may cause runaways. theoremBoostFactor() { - if (!Ra.has(RA_UNLOCKS.TT_BOOST)) return 0; return Math.min(10, Math.max(0, Currency.timeTheorems.value.pLog10() - 350) / 50); }, get isUnlocked() { @@ -289,22 +338,19 @@ export const Ra = { return player.celestials.ra.run; }, get totalCharges() { - return Math.min(12, Math.floor(Ra.pets.teresa.level / 2)); + return Ra.unlocks.chargedInfinityUpgrades.effectOrDefault(0); }, get chargesLeft() { return this.totalCharges - player.celestials.ra.charged.size; }, - get chargeUnlocked() { - return V.has(V_UNLOCKS.RA_UNLOCK) && Ra.pets.teresa.level > 1; - }, get canBuyTriad() { - return this.pets.v.level >= 5; + return Ra.unlocks.unlockHardV.canBeApplied; }, - get petWithRecollection() { - return player.celestials.ra.petWithRecollection; + get petWithRemembrance() { + return player.celestials.ra.petWithRemembrance; }, - set petWithRecollection(name) { - player.celestials.ra.petWithRecollection = name; + set petWithRemembrance(name) { + player.celestials.ra.petWithRemembrance = name; }, updateAlchemyFlow(realityRealTime) { const perSecond = 1000 / realityRealTime; @@ -314,7 +360,7 @@ export const Ra = { } }, applyAlchemyReactions(realityRealTime) { - if (!Ra.has(RA_UNLOCKS.EFFARIG_UNLOCK)) return; + if (!Ra.unlocks.effarigUnlock.canBeApplied) return; const sortedReactions = AlchemyReactions.all .compact() .sort((r1, r2) => r2.priority - r1.priority); @@ -330,136 +376,7 @@ export const Ra = { const hoursFromUnlock = TimeSpan.fromMilliseconds(player.celestials.ra.momentumTime).totalHours; return Math.clampMax(1 + 0.002 * hoursFromUnlock, AlchemyResource.momentum.effectValue); }, - quotes: new CelestialQuotes("ra", { - UNLOCK: { - id: 1, - lines: [ - "A... visitor?", - "I am here! I am the one you are looking for... I think...", - "What even was I again?", - "Oh right, the Celestial of Memories.", - ] - }, - REALITY_ENTER: { - id: 2, - lines: [ - "I have not seen the others in so long...", - "Can you help me remember them?", - "I could give you powers in exchange.", - ] - }, - TERESA_START: { - id: 3, - requirement: () => Ra.pets.teresa.level >= 2, - lines: [ - "Te... re... sa...", - "I think I remember.", - ] - }, - TERESA_LATE: { - id: 4, - requirement: () => Ra.pets.teresa.level >= 15, - lines: [ - "Teresa dealt with machines, I believe.", - "I remember visiting Teresa’s shop a few times.", - "Wait, someone else had a shop too, right?", - ] - }, - EFFARIG_START: { - id: 5, - requirement: () => Ra.pets.effarig.level >= 2, - lines: [ - "Eff... a... rig", - "I remember Effarig being friendly.", - ] - }, - EFFARIG_LATE: { - id: 6, - requirement: () => Ra.pets.effarig.level >= 15, - lines: [ - "Effarig was very particular?", - "And I also remember a frightening Reality...", - "It was about... suffering?", - ] - }, - ENSLAVED_START: { - id: 7, - requirement: () => Ra.pets.enslaved.level >= 2, - lines: [ - "I cannot remember this one completely...", - ] - }, - ENSLAVED_LATE: { - id: 8, - requirement: () => Ra.pets.enslaved.level >= 15, - lines: [ - "I am starting to remember...", - "Why I am here...", - "Why I am alone...", - "Help me.", - ] - }, - V_START: { - id: 9, - requirement: () => Ra.pets.v.level >= 2, - lines: [ - "Had I met this one?", - "So lonely, yet willingly so...", - ] - }, - V_LATE: { - id: 10, - requirement: () => Ra.pets.v.level >= 15, - lines: [ - "I think I met V once...", - "I can remember the achievements.", - ] - }, - RECOLLECTION: { - id: 11, - requirement: () => Ra.has(RA_UNLOCKS.RA_RECOLLECTION_UNLOCK), - lines: [ - "I remembered something!", - "Watch this!", - "Recollection!", - "I can focus even harder on remembering them now!", - ] - }, - MID_MEMORIES: { - id: 12, - requirement: () => Ra.totalPetLevel >= 50, - lines: [ - "Realities are my homes, yet I cannot make my own Reality.", - "I can only copy the ones of my friends.", - "But... why am I hearing voices?", - "Are they asking for help?", - ] - }, - LATE_MEMORIES: { - id: 13, - requirement: () => Ra.totalPetLevel >= 80, - lines: [ - "I think they are telling me to stop.", - "You... whatever you are?", - "What is happening?", - "Am I doing something wrong?", - ] - }, - MAX_LEVELS: { - id: 14, - requirement: () => Ra.totalPetLevel === Ra.maxTotalPetLevel, - lines: [ - "Finally, I remember everything.", - "This darkness that banished me.", - "Lai'tela...", - "They were right to banish me.", - "My powers...", - "They steal, they corrupt.", - "Please leave.", - "I do not want to hurt you too.", - ] - }, - }), + quotes: Quotes.ra, symbol: "" }; @@ -486,7 +403,7 @@ export const GlyphAlteration = { }, get isUnlocked() { if (Pelle.isDisabled("alteration")) return false; - return Ra.has(RA_UNLOCKS.ALTERED_GLYPHS); + return Ra.unlocks.alteredGlyphs.canBeApplied; }, isAdded(type) { return this.isUnlocked && this.getSacrificePower(type) >= this.additionThreshold; @@ -502,285 +419,19 @@ export const GlyphAlteration = { return Math.log10(Math.clampMin(capped / this.boostingThreshold, 1)) / 2; }, getAdditionColor(type) { - return this.isAdded(type) - ? "#CCCCCC" - : undefined; + const color = Theme.current().isDark() ? "#CCCCCC" : "black"; + return this.isAdded(type) ? color : undefined; }, getEmpowermentColor(type) { - return this.isEmpowered(type) - ? "#EEEE30" - : undefined; + const color = Theme.current().isDark() ? "#EEEE30" : "#C6C610"; + return this.isEmpowered(type) ? color : undefined; }, getBoostColor(type) { - return this.isBoosted(type) - ? "#60DDDD" - : undefined; + const color = Theme.current().isDark() ? "#60DDDD" : "#28BDBD"; + return this.isBoosted(type) ? color : undefined; }, }; -export const RA_UNLOCKS = { - AUTO_TP: { - id: 0, - description: "Unlock Teresa", - reward: "Tachyon Particles are given immediately when Time Dilation is active", - pet: Ra.pets.teresa, - level: 1, - displayIcon: `` - }, - CHARGE: { - id: 1, - description: "Get Teresa to level 2", - reward: () => `Unlock Charged Infinity Upgrades. You get one more maximum - Charged Infinity Upgrade every ${formatInt(2)} levels`, - pet: Ra.pets.teresa, - level: 2, - displayIcon: `` - }, - TERESA_XP: { - id: 2, - description: "Get Teresa to level 5", - reward: "All Memory Chunks produce more Memories based on Reality Machines", - pet: Ra.pets.teresa, - level: 5, - displayIcon: `Δ` - }, - ALTERED_GLYPHS: { - id: 3, - description: "Get Teresa to level 8", - reward: "Unlock Altered Glyphs, which grant new effects to Glyphs based on Glyph Sacrifice", - pet: Ra.pets.teresa, - level: 10, - displayIcon: `` - }, - EFFARIG_UNLOCK: { - id: 4, - description: "Get Teresa to level 10", - reward: "Unlock Effarig's Memories", - pet: Ra.pets.teresa, - level: 8, - displayIcon: `Ϙ` - }, - PERK_SHOP_INCREASE: { - id: 5, - description: "Get Teresa to level 15", - reward: "Perk shop caps are raised", - pet: Ra.pets.teresa, - level: 15, - displayIcon: `` - }, - START_TP: { - id: 6, - description: "Get Teresa to level 25", - reward: `When unlocking Time Dilation in non-celestial Realities, gain Tachyon Particles as if you reached - the square root of your total antimatter in Dilation`, - effect: () => player.records.totalAntimatter.pow(0.5), - pet: Ra.pets.teresa, - level: 25, - displayIcon: `` - }, - EXTRA_CHOICES_AND_RELIC_SHARD_RARITY_ALWAYS_MAX: { - id: 7, - description: "Unlock Effarig", - reward: () => `Get ${formatX(2)} Glyph choices and the bonus to Glyph rarity from Relic Shards - is always its maximum value`, - pet: Ra.pets.effarig, - level: 1, - displayIcon: `` - }, - GLYPH_ALCHEMY: { - id: 8, - description: "Get Effarig to level 2", - reward: `Unlock Glyph Alchemy, which adds alchemical resources you can increase by Refining Glyphs. You unlock - more resources through Effarig levels. Access through a new Reality tab.`, - pet: Ra.pets.effarig, - level: 2, - displayIcon: `` - }, - EFFARIG_XP: { - id: 9, - description: "Get Effarig to level 5", - reward: "All Memory Chunks produce more Memories based on highest Glyph level", - pet: Ra.pets.effarig, - level: 5, - displayIcon: `` - }, - GLYPH_EFFECT_COUNT: { - id: 10, - description: "Get Effarig to level 8", - reward: () => `Glyphs always have ${formatInt(4)} effects, and Effarig Glyphs can now have up to ${formatInt(7)}`, - pet: Ra.pets.effarig, - level: 10, - displayIcon: `` - }, - ENSLAVED_UNLOCK: { - id: 11, - description: "Get Effarig to level 10", - reward: "Unlock Enslaved's Memories", - pet: Ra.pets.effarig, - level: 8, - displayIcon: `` - }, - SHARD_LEVEL_BOOST: { - id: 12, - description: "Get Effarig to level 15", - reward: "Glyph level is increased based on Relic Shards gained", - effect: () => 100 * Math.pow(Math.log10(Math.max(Effarig.shardsGained, 1)), 2), - pet: Ra.pets.effarig, - level: 15, - displayIcon: `` - }, - MAX_RARITY_AND_SHARD_SACRIFICE_BOOST: { - id: 13, - description: "Get Effarig to level 25", - reward: () => `Glyphs are always generated with ${formatPercents(1)} rarity and ` + - `Glyph Sacrifice gain is raised to a power based on Relic Shards`, - pet: Ra.pets.effarig, - level: 25, - displayIcon: `` - }, - AUTO_BLACK_HOLE_POWER: { - id: 14, - description: "Unlock Enslaved", - reward: "Unlock Black Hole power upgrade autobuyers", - pet: Ra.pets.enslaved, - level: 1, - displayIcon: `` - }, - IMPROVED_STORED_TIME: { - id: 15, - description: "Get Enslaved to level 2", - reward: "Stored game time is amplified and you can store more real time, increasing with Enslaved levels", - effect: { - gameTimeAmplification: () => Math.pow(20, Math.clampMax(Ra.pets.enslaved.level, Ra.levelCap)), - realTimeCap: () => 1000 * 3600 * Ra.pets.enslaved.level, - }, - pet: Ra.pets.enslaved, - level: 2, - displayIcon: `` - }, - ENSLAVED_XP: { - id: 16, - description: "Get Enslaved to level 5", - reward: "All Memory Chunks produce more Memories based on total time played", - pet: Ra.pets.enslaved, - level: 5, - displayIcon: `` - }, - ADJUSTABLE_STORED_TIME: { - id: 17, - description: "Get Enslaved to level 8", - reward: () => `Black Hole charging can be done at an adjustable rate and automatically - pulsed every ${formatInt(5)} ticks. You can change these in the Black Hole and The Enslaved Ones' tabs`, - pet: Ra.pets.enslaved, - level: 10, - displayIcon: `` - }, - V_UNLOCK: { - id: 18, - description: "Get Enslaved to level 10", - reward: "Unlock V's Memories", - pet: Ra.pets.enslaved, - level: 8, - displayIcon: `⌬` - }, - PEAK_GAMESPEED: { - id: 19, - description: "Get Enslaved to level 15", - reward: "Gain more Dilated Time based on peak game speed in each Reality", - pet: Ra.pets.enslaved, - level: 15, - displayIcon: `` - }, - ALWAYS_GAMESPEED: { - id: 20, - description: "Get Enslaved to level 25", - reward: `All basic Glyphs gain the increased game speed effect from Time Glyphs, - and Time Glyphs gain an additional effect`, - pet: Ra.pets.enslaved, - level: 25, - displayIcon: `` - }, - AUTO_RU_AND_INSTANT_EC: { - id: 21, - description: "Unlock V", - reward: "The rebuyable Reality upgrades are bought automatically and Auto-Eternity Challenges happen instantly", - pet: Ra.pets.v, - level: 1, - displayIcon: `` - }, - AUTO_DILATION_UNLOCK: { - id: 22, - description: "Get V to level 2", - reward: () => `Time Dilation is unlocked automatically for free at - ${formatInt(TimeStudy.dilation.totalTimeTheoremRequirement)} Time Theorems outside of Celestial Realities`, - pet: Ra.pets.v, - level: 2, - displayIcon: `` - }, - V_XP: { - id: 23, - description: "Get V to level 5", - reward: () => `All Memory Chunks produce more Memories based on total Celestial levels, - and unlock a Triad Study every ${formatInt(5)} levels (to a maximum of ${formatInt(4)} Triad Studies). - Triad Studies are located at the bottom of the Time Studies page`, - pet: Ra.pets.v, - level: 5, - displayIcon: `` - }, - HARD_V: { - id: 24, - description: "Get V to level 8", - reward: "Unlock Hard V-Achievements", - pet: Ra.pets.v, - level: 8, - displayIcon: `` - }, - TT_BOOST: { - id: 25, - description: "Get V to level 10", - reward: "Time Theorems boost all forms of continuous non-dimension production", - effect: { - // All of these are accessed directly from RA_UNLOCKS across much of the game, but are effectively dummied out - // before the upgrade itself is unlocked due to theoremBoostFactor evaluating to zero if the upgrade is missing. - ttGen: () => Math.pow(10, 5 * Ra.theoremBoostFactor()), - eternity: () => Math.pow(10, 2 * Ra.theoremBoostFactor()), - infinity: () => Math.pow(10, 15 * Ra.theoremBoostFactor()), - replicanti: () => Math.pow(10, 20 * Ra.theoremBoostFactor()), - dilatedTime: () => Math.pow(10, 3 * Ra.theoremBoostFactor()), - memories: () => 1 + Ra.theoremBoostFactor() / 50, - memoryChunks: () => 1 + Ra.theoremBoostFactor() / 50, - autoPrestige: () => 1 + 2.4 * Ra.theoremBoostFactor() - }, - pet: Ra.pets.v, - level: 10, - displayIcon: `` - }, - TT_ACHIEVEMENT: { - id: 26, - description: "Get V to level 15", - reward: "Achievement multiplier applies to Time Theorem generation", - effect: () => Achievements.power, - pet: Ra.pets.v, - level: 15, - displayIcon: `` - }, - ACHIEVEMENT_POW: { - id: 27, - description: "Get V to level 25", - reward: () => `Achievement multiplier is raised ${formatPow(1.5, 1, 1)}`, - pet: Ra.pets.v, - level: 25, - displayIcon: `` - }, - RA_RECOLLECTION_UNLOCK: { - id: 28, - description: "Get 20 total Celestial Memory levels", - reward: "Unlock Recollection", - effect: 3, - totalLevels: 20, - } -}; - EventHub.logic.on(GAME_EVENT.TAB_CHANGED, () => { - if (Tab.celestials.ra.isOpen) Ra.quotes.show(Ra.quotes.UNLOCK); + if (Tab.celestials.ra.isOpen) Ra.quotes.unlock.show(); }); diff --git a/javascripts/core/celestials/teresa.js b/javascripts/core/celestials/teresa.js index e547ba434..241243562 100644 --- a/javascripts/core/celestials/teresa.js +++ b/javascripts/core/celestials/teresa.js @@ -1,52 +1,14 @@ -import { GameDatabase } from "../secret-formula/game-database.js"; -import { RebuyableMechanicState } from "../game-mechanics/index.js"; -import { CelestialQuotes } from "./quotes.js"; +import { BitUpgradeState, RebuyableMechanicState } from "../game-mechanics/index"; +import { GameDatabase } from "../secret-formula/game-database"; -export const TERESA_UNLOCKS = { - RUN: { - id: 0, - price: 1e14, - description: "Unlock Teresa's Reality.", - }, - EPGEN: { - id: 1, - price: 1e18, - get description() { - if (Pelle.isDoomed) return "This has no effect while in Doomed."; - return "Unlock passive Eternity Point generation."; - }, - }, - EFFARIG: { - id: 2, - price: 1e21, - description: "Unlock Effarig, Celestial of Ancient Relics.", - }, - SHOP: { - id: 3, - price: 1e24, - description: "Unlock the Perk Point Shop.", - }, - UNDO: { - id: 4, - price: 1e10, - description: "Unlock \"Undo\" of equipping a Glyph.", - }, - START_EU: { - id: 5, - price: 1e6, - get description() { - if (Pelle.isDoomed) return "This has no effect while in Doomed."; - return "You start Reality with all Eternity Upgrades unlocked."; - }, - } -}; +import { Quotes } from "./quotes"; export const Teresa = { timePoured: 0, - unlockInfo: TERESA_UNLOCKS, - lastUnlock: "SHOP", + lastUnlock: "shop", pouredAmountCap: 1e24, displayName: "Teresa", + possessiveName: "Teresa's", get isUnlocked() { return Achievement(147).isUnlocked; }, @@ -60,19 +22,10 @@ export const Teresa = { this.checkForUnlocks(); }, checkForUnlocks() { - for (const info of Object.values(Teresa.unlockInfo)) { - if (!this.has(info) && this.pouredAmount >= info.price) { - // eslint-disable-next-line no-bitwise - player.celestials.teresa.unlockBits |= (1 << info.id); - EventHub.dispatch(GAME_EVENT.CELESTIAL_UPGRADE_UNLOCKED, this, info); - } + for (const info of TeresaUnlocks.all) { + info.unlock(); } }, - has(info) { - if (!info.hasOwnProperty("id")) throw "Pass in the whole TERESA UNLOCK object"; - // eslint-disable-next-line no-bitwise - return Boolean(player.celestials.teresa.unlockBits & (1 << info.id)); - }, initializeRun() { clearCelestialRuns(); player.celestials.teresa.run = true; @@ -104,31 +57,7 @@ export const Teresa = { get runCompleted() { return player.celestials.teresa.bestRunAM.gt(0); }, - quotes: new CelestialQuotes("teresa", { - INITIAL: { - id: 1, - lines: [ - "We have been observing you.", - "You have shown promise with your bending of Reality.", - "We are the Celestials, and we want you to join us.", - "My name is Teresa, the Celestial Of Reality.", - "Prove your worth.", - ] - }, - UNLOCK_REALITY: CelestialQuotes.singleLine( - 2, "I will let you inside my Reality, mortal. Do not get crushed by it." - ), - COMPLETE_REALITY: CelestialQuotes.singleLine( - 3, "Why are you still here... you were supposed to fail." - ), - EFFARIG: { - id: 4, - lines: [ - "You are still no match for us.", - "I hope the others succeed where I have failed." - ] - } - }), + quotes: Quotes.teresa, symbol: "Ϟ" }; @@ -160,12 +89,15 @@ class PerkShopUpgradeState extends RebuyableMechanicState { } onPurchased() { + if (this.id === 0) { + GameCache.staticGlyphWeights.invalidate(); + } if (this.id === 1) { Autobuyer.reality.bumpAmount(2); } // Give a single music glyph - if (this.id === 4) { - if (Glyphs.freeInventorySpace === 0) { + if (this.id === 4 && !Pelle.isDoomed) { + if (GameCache.glyphInventorySpace.value === 0) { // Refund the perk point if they didn't actually get a glyph Currency.perkPoints.add(1); GameUI.notify.error("You have no empty inventory space!"); @@ -175,35 +107,55 @@ class PerkShopUpgradeState extends RebuyableMechanicState { } } // Fill the inventory with music glyphs - if (this.id === 5) { - const toCreate = Glyphs.freeInventorySpace; + if (this.id === 5 && !Pelle.isDoomed) { + const toCreate = GameCache.glyphInventorySpace.value; for (let count = 0; count < toCreate; count++) Glyphs.addToInventory(GlyphGenerator.musicGlyph()); GameUI.notify.success(`Created ${quantifyInt("Music Glyph", toCreate)}`); } } } -export const PerkShopUpgrade = (function() { - const db = GameDatabase.celestials.perkShop; - return { - glyphLevel: new PerkShopUpgradeState(db.glyphLevel), - rmMult: new PerkShopUpgradeState(db.rmMult), - bulkDilation: new PerkShopUpgradeState(db.bulkDilation), - autoSpeed: new PerkShopUpgradeState(db.autoSpeed), - musicGlyph: new PerkShopUpgradeState(db.musicGlyph), - fillMusicGlyph: new PerkShopUpgradeState(db.fillMusicGlyph), - }; -}()); +class TeresaUnlockState extends BitUpgradeState { + get bits() { return player.celestials.teresa.unlockBits; } + set bits(value) { player.celestials.teresa.unlockBits = value; } + + get price() { + return this.config.price; + } + + get pelleDisabled() { + return Pelle.isDoomed && this.config.isDisabledInDoomed; + } + + get isEffectActive() { + return !this.pelleDisabled; + } + + get canBeUnlocked() { + return !this.isUnlocked && Teresa.pouredAmount >= this.price; + } + + get description() { + return typeof this.config.description === "function" ? this.config.description() : this.config.description; + } + + onUnlock() { + this.config.onUnlock?.(); + } +} + +export const TeresaUnlocks = mapGameDataToObject( + GameDatabase.celestials.teresa.unlocks, + config => new TeresaUnlockState(config) +); + +export const PerkShopUpgrade = mapGameDataToObject( + GameDatabase.celestials.perkShop, + config => new PerkShopUpgradeState(config) +); EventHub.logic.on(GAME_EVENT.TAB_CHANGED, () => { - if (Tab.celestials.teresa.isOpen) Teresa.quotes.show(Teresa.quotes.INITIAL); -}); - -EventHub.logic.on(GAME_EVENT.CELESTIAL_UPGRADE_UNLOCKED, ([celestial, upgradeInfo]) => { - if (celestial === Teresa) { - if (upgradeInfo === TERESA_UNLOCKS.RUN) Teresa.quotes.show(Teresa.quotes.UNLOCK_REALITY); - if (upgradeInfo === TERESA_UNLOCKS.EFFARIG) Teresa.quotes.show(Teresa.quotes.EFFARIG); - } + if (Tab.celestials.teresa.isOpen) Teresa.quotes.initial.show(); }); EventHub.logic.on(GAME_EVENT.GAME_LOAD, () => Teresa.checkForUnlocks()); diff --git a/javascripts/core/constants.js b/javascripts/core/constants.js index 8e7e703ac..31d4faed7 100644 --- a/javascripts/core/constants.js +++ b/javascripts/core/constants.js @@ -229,38 +229,105 @@ window.GlyphRarities = [ { minStrength: 3.5, name: "Celestial", - color: "#5151ec" + darkColor: "#5151ec", + lightColor: "#6666e9" }, { minStrength: 3.25, name: "Transcendent", - color: "#03ffec" + darkColor: "#03ffec", + lightColor: "#00bdad" }, { minStrength: 3, name: "Mythical", - color: "#d50000" + darkColor: "#d50000", + lightColor: "#d50000" }, { minStrength: 2.75, name: "Legendary", - color: "#ff9800" + darkColor: "#ff9800", + lightColor: "#d68100" }, { minStrength: 2.5, name: "Epic", - color: "#9c27b0" + darkColor: "#9c27b0", + lightColor: "#9c27b0" }, { minStrength: 2, name: "Rare", - color: "#2196f3" + darkColor: "#2196f3", + lightColor: "#1187ee" }, { minStrength: 1.5, name: "Uncommon", - color: "#43a047" + darkColor: "#43a047", + lightColor: "#3c9040" }, { minStrength: 1, name: "Common", - color: "white" + darkColor: "white", + lightColor: "black" }, ]; +window.GLYPH_TYPES = [ + "power", + "infinity", + "replication", + "time", + "dilation", + "effarig", + "reality", + "cursed", + "companion" +]; + +window.BASIC_GLYPH_TYPES = [ + "power", + "infinity", + "replication", + "time", + "dilation" +]; + +window.ALCHEMY_BASIC_GLYPH_TYPES = [ + "power", + "infinity", + "replication", + "time", + "dilation", + "effarig" +]; + +window.GLYPH_SYMBOLS = { + power: "Ω", + infinity: "∞", + replication: "Ξ", + time: "Δ", + dilation: "Ψ", + effarig: "Ϙ", + reality: "Ϟ", + cursed: "⸸", + companion: "♥" +}; + +window.CANCER_GLYPH_SYMBOLS = { + power: "⚡", + infinity: "8", + replication: "⚤", + time: "🕟", + dilation: "☎", + effarig: "🦒", + reality: "⛧", + cursed: "☠", + companion: "³" +}; + +window.ALTERATION_TYPE = { + ADDITION: 1, + EMPOWER: 2, + BOOST: 3 +}; + window.BLACK_HOLE_PAUSE_MODE = { NO_PAUSE: 0, PAUSE_BEFORE_BH1: 1, @@ -371,3 +438,28 @@ window.SORT_ORDER = { ASCENDING: 0, DESCENDING: 1, }; + +// One-indexed and ordered to simplify code elsewhere, do not change to be zero-indexed or reorder +window.PROGRESS_STAGE = { + PRE_INFINITY: 1, + + EARLY_INFINITY: 2, + BREAK_INFINITY: 3, + REPLICANTI: 4, + + EARLY_ETERNITY: 5, + ETERNITY_CHALLENGES: 6, + EARLY_DILATION: 7, + LATE_ETERNITY: 8, + + EARLY_REALITY: 9, + + TERESA: 10, + EFFARIG: 11, + ENSLAVED: 12, + V: 13, + RA: 14, + IMAGINARY_MACHINES: 15, + LAITELA: 16, + PELLE: 17, +}; diff --git a/javascripts/core/crash.js b/javascripts/core/crash.js index 6e0cca269..b0456b86c 100644 --- a/javascripts/core/crash.js +++ b/javascripts/core/crash.js @@ -35,11 +35,14 @@ window.GlobalErrorHandler = { }, crash(message) { if (window.GameUI !== undefined && GameUI.initialized) { - Modal.message.show(`${message}
Check the console for more details`); + Modal.message.show(`${message}
Check the console for more details`, {}, 3); } // eslint-disable-next-line no-debugger debugger; } }; -window.onerror = event => GlobalErrorHandler.onerror(event); +window.onerror = (event, source) => { + if (!source.endsWith(".js")) return; + GlobalErrorHandler.onerror(event); +}; diff --git a/javascripts/core/currency.js b/javascripts/core/currency.js index 695514a03..1528c734a 100644 --- a/javascripts/core/currency.js +++ b/javascripts/core/currency.js @@ -1,4 +1,4 @@ -import { DC } from "./constants.js"; +import { DC } from "./constants"; /** @@ -200,6 +200,10 @@ Currency.antimatter = new class extends DecimalCurrency { get value() { return player.antimatter; } set value(value) { + if (InfinityChallenges.nextIC) InfinityChallenges.notifyICUnlock(value); + if (GameCache.cheapestAntimatterAutobuyer.value && value.gte(GameCache.cheapestAntimatterAutobuyer.value)) { + TabNotification.newAutobuyer.tryTrigger(); + } player.antimatter = value; player.records.thisInfinity.maxAM = player.records.thisInfinity.maxAM.max(value); player.records.thisEternity.maxAM = player.records.thisEternity.maxAM.max(value); diff --git a/javascripts/core/devtools.js b/javascripts/core/devtools.js index e1ea9ed91..417327503 100644 --- a/javascripts/core/devtools.js +++ b/javascripts/core/devtools.js @@ -1,6 +1,8 @@ -import { DC } from "./constants.js"; import { sha512_256 } from "js-sha512"; +import { DC } from "./constants"; +import FullScreenAnimationHandler from "./full-screen-animation-handler"; + /* eslint-disable no-console */ // Disabling no-console here seems // reasonable, since these are the devtools after all @@ -92,22 +94,21 @@ dev.tripleEverything = function() { }; dev.barrelRoll = function() { - document.body.style.animation = "barrelRoll 5s 1"; - setTimeout(() => document.body.style.animation = "", 5000); + FullScreenAnimationHandler.display("a-barrel-roll", 5); }; dev.spin3d = function() { - if (document.body.style.animation === "") document.body.style.animation = "spin3d 3s infinite"; + if (document.body.style.animation === "") document.body.style.animation = "a-spin3d 3s infinite"; else document.body.style.animation = ""; }; dev.spin4d = function() { - if (document.body.style.animation === "") document.body.style.animation = "spin4d 3s infinite"; + if (document.body.style.animation === "") document.body.style.animation = "a-spin4d 3s infinite"; else document.body.style.animation = ""; }; dev.cancerize = function() { - Theme.tryUnlock("Cancer"); + Theme.tryUnlock("Design"); Notation.emoji.setAsCurrent(); }; @@ -165,8 +166,8 @@ dev.resetDilation = function() { // when making a special glyph, so no max-params // eslint-disable-next-line max-params dev.giveSpecialGlyph = function(color, symbol, level, rawLevel = level) { - if (!specialGlyphSymbols.hasOwnProperty(symbol)) return; - if (Glyphs.freeInventorySpace === 0) return; + if (!Object.prototype.hasOwnProperty.call(specialGlyphSymbols, symbol)) return; + if (GameCache.glyphInventorySpace.value === 0) return; const glyph = GlyphGenerator.randomGlyph({ actualLevel: level, rawLevel }); glyph.symbol = symbol; glyph.color = color; @@ -174,12 +175,12 @@ dev.giveSpecialGlyph = function(color, symbol, level, rawLevel = level) { }; dev.giveGlyph = function(level, rawLevel = level) { - if (Glyphs.freeInventorySpace === 0) return; + if (GameCache.glyphInventorySpace.value === 0) return; Glyphs.addToInventory(GlyphGenerator.randomGlyph({ actualLevel: level, rawLevel })); }; dev.giveRealityGlyph = function(level) { - if (Glyphs.freeInventorySpace === 0) return; + if (GameCache.glyphInventorySpace.value === 0) return; Glyphs.addToInventory(GlyphGenerator.realityGlyph(level)); }; @@ -349,11 +350,11 @@ dev.printResourceTotals = function() { }; dev.unlockCelestialQuotes = function(celestial) { - const quotes = Celestials[celestial].quotes; - for (const q of quotes.quotesById) { - if (q === undefined) continue; - quotes.show(q); - } + Quotes[celestial].all.forEach(x => x.show()); +}; + +dev.presentCelestialQuotes = function(celestial) { + Quotes[celestial].all.forEach(x => x.present()); }; // This doesn't check everything but hopefully it gets some of the more obvious ones. @@ -416,19 +417,16 @@ dev.testReplicantiCode = function(singleId, useDebugger = false) { ], [ function() { - // eslint-disable-next-line no-bitwise player.achievementBits[8] |= 16; } ], [ function() { - // eslint-disable-next-line no-bitwise player.achievementBits[12] |= 8; } ], [ function() { - // eslint-disable-next-line no-bitwise player.achievementBits[12] |= 128; } ], @@ -458,7 +456,6 @@ dev.testReplicantiCode = function(singleId, useDebugger = false) { ], [ function() { - // eslint-disable-next-line no-bitwise player.reality.upgReqs = (1 << 6); player.reality.upgradeBits = 64; } @@ -594,11 +591,20 @@ dev.testGlyphs = function(config) { runTrial(0); }; -dev.devMode = function() { - player.devMode = !player.devMode; -}; - // May want to make this command in particular publicly known if automator gating is a common complaint post-release dev.unlockAutomator = function() { player.reality.automator.forceUnlock = true; }; + +// This bypasses any conflict checking and forces the current save to overwrite the cloud save. This largely exists +// because normal cloud saving checks for a conflict and then always shows a modal if a conflict is found, only actually +// saving if the player says to in the modal. The check can fail if the cloud save is somehow malformed and missing +// props. This can lead to the check always failing, the modal never showing up, and cloud saving never occurring. That +// should in principle only show up in dev, as migrations aren't run on cloud saves, but this allows fixing in case. +dev.forceCloudSave = async function() { + const save = await Cloud.load(); + const root = GameSaveSerializer.deserialize(save); + const saveId = GameStorage.currentSlot; + root.saves[saveId] = GameStorage.saves[saveId]; + Cloud.save(saveId); +}; diff --git a/javascripts/core/dilation.js b/javascripts/core/dilation.js index 75f7e8961..32fa6e464 100644 --- a/javascripts/core/dilation.js +++ b/javascripts/core/dilation.js @@ -1,20 +1,15 @@ -import { SetPurchasableMechanicState, RebuyableMechanicState } from "./game-mechanics/index.js"; -import { DC } from "./constants.js"; -import { SpeedrunMilestones } from "./speedrun.js"; +import { RebuyableMechanicState, SetPurchasableMechanicState } from "./game-mechanics/index"; +import { DC } from "./constants"; +import FullScreenAnimationHandler from "./full-screen-animation-handler"; +import { SpeedrunMilestones } from "./speedrun"; export function animateAndDilate() { - document.body.style.animation = "dilate 2s 1 linear"; - setTimeout(() => { - document.body.style.animation = ""; - }, 2000); + FullScreenAnimationHandler.display("a-dilate", 2); setTimeout(startDilatedEternity, 1000); } export function animateAndUndilate() { - document.body.style.animation = "undilate 2s 1 linear"; - setTimeout(() => { - document.body.style.animation = ""; - }, 2000); + FullScreenAnimationHandler.display("a-undilate", 2); setTimeout(() => { eternity(false, false, { switchingDilation: true }); }, 1000); @@ -22,10 +17,11 @@ export function animateAndUndilate() { export function startDilatedEternityRequest() { if (!PlayerProgress.dilationUnlocked() || (Pelle.isDoomed && !Pelle.canDilateInPelle)) return; - const playAnimation = player.options.animations.dilation && document.body.style.animation === ""; + const playAnimation = player.options.animations.dilation && !FullScreenAnimationHandler.isDisplaying; if (player.dilation.active) { - // TODO Dilation modal - if (playAnimation) { + if (player.options.confirmations.dilation) { + Modal.exitDilation.show(); + } else if (playAnimation) { animateAndUndilate(); } else { eternity(false, false, { switchingDilation: true }); @@ -37,20 +33,20 @@ export function startDilatedEternityRequest() { } else { startDilatedEternity(); } - if (Pelle.isDoomed && !player.options.confirmations.dilation) { - PelleStrikes.dilation.trigger(); - } } export function startDilatedEternity(auto) { - if (!PlayerProgress.dilationUnlocked()) return; + if (!PlayerProgress.dilationUnlocked()) return false; + if (GameEnd.creditsEverClosed) return false; if (player.dilation.active) { eternity(false, auto, { switchingDilation: true }); - return; + return false; } Achievement(136).unlock(); eternity(false, auto, { switchingDilation: true }); player.dilation.active = true; + if (Pelle.isDoomed) PelleStrikes.dilation.trigger(); + return true; } const DIL_UPG_NAMES = [ @@ -60,6 +56,7 @@ const DIL_UPG_NAMES = [ ]; export function buyDilationUpgrade(id, bulk = 1) { + if (GameEnd.creditsEverClosed) return false; // Upgrades 1-3 are rebuyable, and can be automatically bought in bulk with a perk shop upgrade const upgrade = DilationUpgrade[DIL_UPG_NAMES[id]]; if (id > 3 && id < 11) { @@ -114,12 +111,12 @@ export function getTachyonGalaxyMult(thresholdUpgrade) { } export function getDilationGainPerSecond() { - const mult = NG.multiplier; if (Pelle.isDoomed) { - const tachyonEffect = Currency.tachyonParticles.value.pow(PelleRifts.death.milestones[1].effectOrDefault(1)); + const tachyonEffect = Currency.tachyonParticles.value.pow(PelleRifts.paradox.milestones[1].effectOrDefault(1)); return new Decimal(tachyonEffect) .timesEffectsOf(DilationUpgrade.dtGain, DilationUpgrade.dtGainPelle, DilationUpgrade.flatDilationMult) - .times(Pelle.specialGlyphEffect.dilation).div(3e4).times(mult); + .times(ShopPurchase.dilatedTimePurchases.currentMult ** 0.5) + .times(Pelle.specialGlyphEffect.dilation).div(3e4); } let dtRate = new Decimal(Currency.tachyonParticles.value) .timesEffectsOf( @@ -127,21 +124,22 @@ export function getDilationGainPerSecond() { Achievement(132), Achievement(137), RealityUpgrade(1), - AlchemyResource.dilation + AlchemyResource.dilation, + Ra.unlocks.continuousTTBoost.effects.dilatedTime, + Ra.unlocks.peakGamespeedDT ); dtRate = dtRate.times(getAdjustedGlyphEffect("dilationDT")); + dtRate = dtRate.times(ShopPurchase.dilatedTimePurchases.currentMult); dtRate = dtRate.times( Math.clampMin(Decimal.log10(Replicanti.amount) * getAdjustedGlyphEffect("replicationdtgain"), 1)); - dtRate = dtRate.times(Ra.gamespeedDTMult()); if (Enslaved.isRunning && !dtRate.eq(0)) dtRate = Decimal.pow10(Math.pow(dtRate.plus(1).log10(), 0.85) - 1); - dtRate = dtRate.times(RA_UNLOCKS.TT_BOOST.effect.dilatedTime()); - dtRate = dtRate.times(mult); if (V.isRunning) dtRate = dtRate.pow(0.5); return dtRate; } function tachyonGainMultiplier() { if (Pelle.isDisabled("tpMults")) return new Decimal(1); + const pow = Enslaved.isRunning ? Enslaved.tachyonNerf : 1; return DC.D1.timesEffectsOf( DilationUpgrade.tachyonGain, GlyphSacrifice.dilation, @@ -149,7 +147,7 @@ function tachyonGainMultiplier() { RealityUpgrade(4), RealityUpgrade(8), RealityUpgrade(15) - ); + ).pow(pow); } export function rewardTP() { @@ -157,13 +155,22 @@ export function rewardTP() { player.dilation.lastEP = Currency.eternityPoints.value; } +// This function exists to apply Teresa-25 in a consistent way; TP multipliers can be very volatile and +// applying the reward only once upon unlock promotes min-maxing the upgrade by unlocking dilation with +// TP multipliers as large as possible. Applying the reward to a base TP value and letting the multipliers +// act dynamically on this fixed base value elsewhere solves that issue +export function getBaseTP(antimatter) { + const am = (isInCelestialReality() || Pelle.isDoomed) + ? antimatter + : Ra.unlocks.unlockDilationStartingTP.effectOrDefault(antimatter); + let baseTP = Decimal.pow(Decimal.log10(am) / 400, 1.5); + if (Enslaved.isRunning) baseTP = baseTP.pow(Enslaved.tachyonNerf); + return baseTP; +} + // Returns the TP that would be gained this run export function getTP(antimatter) { - let tachyon = Decimal - .pow(Decimal.log10(antimatter) / 400, 1.5) - .times(tachyonGainMultiplier()); - if (Enslaved.isRunning) tachyon = tachyon.pow(Enslaved.tachyonNerf); - return tachyon; + return getBaseTP(antimatter).times(tachyonGainMultiplier()); } // Returns the amount of TP gained, subtracting out current TP; used only for displaying gained TP @@ -227,26 +234,12 @@ class RebuyableDilationUpgradeState extends RebuyableMechanicState { } } -export const DilationUpgrade = (function() { - const db = GameDatabase.eternity.dilation; - return { - dtGain: new RebuyableDilationUpgradeState(db.dtGain), - galaxyThreshold: new RebuyableDilationUpgradeState(db.galaxyThreshold), - tachyonGain: new RebuyableDilationUpgradeState(db.tachyonGain), - doubleGalaxies: new DilationUpgradeState(db.doubleGalaxies), - tdMultReplicanti: new DilationUpgradeState(db.tdMultReplicanti), - ndMultDT: new DilationUpgradeState(db.ndMultDT), - ipMultDT: new DilationUpgradeState(db.ipMultDT), - timeStudySplit: new DilationUpgradeState(db.timeStudySplit), - dilationPenalty: new DilationUpgradeState(db.dilationPenalty), - ttGenerator: new DilationUpgradeState(db.ttGenerator), - dtGainPelle: new RebuyableDilationUpgradeState(db.dtGainPelle), - galaxyMultiplier: new RebuyableDilationUpgradeState(db.galaxyMultiplier), - tickspeedPower: new RebuyableDilationUpgradeState(db.tickspeedPower), - galaxyThresholdPelle: new DilationUpgradeState(db.galaxyThresholdPelle), - flatDilationMult: new DilationUpgradeState(db.flatDilationMult), - }; -}()); +export const DilationUpgrade = mapGameDataToObject( + GameDatabase.eternity.dilation, + config => (config.rebuyable + ? new RebuyableDilationUpgradeState(config) + : new DilationUpgradeState(config)) +); export const DilationUpgrades = { rebuyable: [ @@ -254,11 +247,5 @@ export const DilationUpgrades = { DilationUpgrade.galaxyThreshold, DilationUpgrade.tachyonGain, ], - fromId: (function() { - const upgradesById = []; - for (const upgrade of Object.values(DilationUpgrade)) { - upgradesById[upgrade.id] = upgrade; - } - return id => upgradesById[id]; - }()), + fromId: id => DilationUpgrade.all.find(x => x.id === Number(id)) }; diff --git a/javascripts/core/dimboost.js b/javascripts/core/dimboost.js index a3057c1e6..e36ba4910 100644 --- a/javascripts/core/dimboost.js +++ b/javascripts/core/dimboost.js @@ -1,4 +1,4 @@ -import { DC } from "./constants.js"; +import { DC } from "./constants"; class DimBoostRequirement { constructor(tier, amount) { @@ -32,7 +32,7 @@ export class DimBoost { Achievement(117), Achievement(142), GlyphEffect.dimBoostPower, - PelleRifts.war.milestones[0] + PelleRifts.recursion.milestones[0] ).powEffectsOf(InfinityUpgrade.dimboostMult.chargedEffect); if (GlyphAlteration.isAdded("effarig")) boost = boost.pow(getSecondaryGlyphEffect("effarigforgotten")); return boost; @@ -141,15 +141,15 @@ export class DimBoost { if (boosts >= DimBoost.maxDimensionsUnlockable - 1) dimensionRange = `to all Dimensions`; let boostEffects; - if (NormalChallenge(8).isRunning) boostEffects = newUnlock === "" ? "" : ` to ${newUnlock}`; - else if (newUnlock === "") boostEffects = ` to ${formattedMultText} ${dimensionRange}`; - else boostEffects = ` to ${newUnlock} and ${formattedMultText} ${dimensionRange}`; + if (NormalChallenge(8).isRunning) boostEffects = newUnlock; + else if (newUnlock === "") boostEffects = `${formattedMultText} ${dimensionRange}`; + else boostEffects = `${newUnlock} and ${formattedMultText} ${dimensionRange}`; - const areDimensionsReset = `Reset - ${((Perk.antimatterNoReset.isBought || Achievement(111).isUnlocked) && - (!Pelle.isDoomed || PelleUpgrade.dimBoostResetsNothing.isBought)) ? "nothing" : "your Dimensions"}`; - - return `${areDimensionsReset}${boostEffects}`; + if (boostEffects === "") return "Dimension Boosts are currently useless"; + const areDimensionsKept = (Perk.antimatterNoReset.isBought || Achievement(111).canBeApplied) && + (!Pelle.isDoomed || PelleUpgrade.dimBoostResetsNothing.isBought); + if (areDimensionsKept) return boostEffects[0].toUpperCase() + boostEffects.substring(1); + return `Reset your Dimensions to ${boostEffects}`; } static get purchasedBoosts() { @@ -165,22 +165,24 @@ export class DimBoost { } } -export function softReset(bulk, forcedNDReset = false, forcedAMReset = false) { +export function softReset(tempBulk, forcedNDReset = false, forcedAMReset = false) { if (Currency.antimatter.gt(Player.infinityLimit)) return; + const bulk = Math.min(tempBulk, DimBoost.maxBoosts - player.dimensionBoosts); EventHub.dispatch(GAME_EVENT.DIMBOOST_BEFORE, bulk); player.dimensionBoosts = Math.max(0, player.dimensionBoosts + bulk); resetChallengeStuff(); - if ( - forcedNDReset || - !Perk.antimatterNoReset.isBought || - (Pelle.isDoomed && !PelleUpgrade.dimBoostResetsNothing.canBeApplied) - ) { + const canKeepDimensions = Pelle.isDoomed + ? PelleUpgrade.dimBoostResetsNothing.canBeApplied + : Perk.antimatterNoReset.canBeApplied; + if (forcedNDReset || !canKeepDimensions) { AntimatterDimensions.reset(); player.sacrificed = DC.D0; resetTickspeed(); } skipResetsIfPossible(); - const canKeepAntimatter = (Achievement(111).isUnlocked || Perk.antimatterNoReset.isBought) && !Pelle.isDoomed; + const canKeepAntimatter = Pelle.isDoomed + ? PelleUpgrade.dimBoostResetsNothing.canBeApplied + : (Achievement(111).isUnlocked || Perk.antimatterNoReset.canBeApplied); if (!forcedAMReset && canKeepAntimatter) { Currency.antimatter.bumpTo(Currency.antimatter.startingValue); } else { @@ -199,10 +201,21 @@ export function skipResetsIfPossible() { else if (InfinityUpgrade.skipReset1.isBought && player.dimensionBoosts < 1) player.dimensionBoosts = 1; } +export function manualRequestDimensionBoost(bulk) { + if (Currency.antimatter.gt(Player.infinityLimit) || !DimBoost.requirement.isSatisfied) return; + if (!DimBoost.canBeBought) return; + if (GameEnd.creditsEverClosed) return; + if (player.options.confirmations.dimensionBoost) { + Modal.dimensionBoost.show({ bulk }); + return; + } + requestDimensionBoost(bulk); +} + export function requestDimensionBoost(bulk) { if (Currency.antimatter.gt(Player.infinityLimit) || !DimBoost.requirement.isSatisfied) return; if (!DimBoost.canBeBought) return; - if (BreakInfinityUpgrade.autobuyMaxDimboosts.isBought && bulk) maxBuyDimBoosts(true); + if (BreakInfinityUpgrade.autobuyMaxDimboosts.isBought && bulk) maxBuyDimBoosts(); else softReset(1); } diff --git a/javascripts/core/dimensions/antimatter-dimension.js b/javascripts/core/dimensions/antimatter-dimension.js index 9e1326b92..1d4f1a599 100644 --- a/javascripts/core/dimensions/antimatter-dimension.js +++ b/javascripts/core/dimensions/antimatter-dimension.js @@ -1,5 +1,6 @@ -import { DimensionState } from "./dimension.js"; -import { DC } from "../constants.js"; +import { DC } from "../constants"; + +import { DimensionState } from "./dimension"; // Multiplier applied to all Antimatter Dimensions, regardless of tier. This is cached using a Lazy // and invalidated every update. @@ -9,7 +10,6 @@ export function antimatterDimensionCommonMultiplier() { multiplier = multiplier.times(Achievements.power); multiplier = multiplier.times(ShopPurchase.dimPurchases.currentMult); multiplier = multiplier.times(ShopPurchase.allDimPurchases.currentMult); - multiplier = multiplier.times(NG.multiplier); if (!EternityChallenge(9).isRunning) { multiplier = multiplier.times(Currency.infinityPower.value.pow(InfinityDimensions.powerConversionRate).max(1)); @@ -54,7 +54,6 @@ export function antimatterDimensionCommonMultiplier() { export function getDimensionFinalMultiplierUncached(tier) { if (tier < 1 || tier > 8) throw new Error(`Invalid Antimatter Dimension tier ${tier}`); - if (Laitela.isRunning && tier > Laitela.maxAllowedDimension) return DC.D0; if (NormalChallenge(10).isRunning && tier > 6) return DC.D1; if (EternityChallenge(11).isRunning) { return Currency.infinityPower.value.pow( @@ -82,7 +81,7 @@ export function getDimensionFinalMultiplierUncached(tier) { } // This power effect goes intentionally after all the nerf effects and shouldn't be moved before them - if (Ra.has(RA_UNLOCKS.EFFARIG_UNLOCK) && multiplier.gte(AlchemyResource.inflation.effectValue)) { + if (AlchemyResource.inflation.isUnlocked && multiplier.gte(AlchemyResource.inflation.effectValue)) { multiplier = multiplier.pow(1.05); } @@ -147,8 +146,6 @@ function applyNDPowers(mult, tier) { const glyphPowMultiplier = getAdjustedGlyphEffect("powerpow"); const glyphEffarigPowMultiplier = getAdjustedGlyphEffect("effarigdimensions"); - multiplier = multiplier.pow(NG.power); - if (InfinityChallenge(4).isRunning && player.postC4Tier !== tier) { multiplier = multiplier.pow(InfinityChallenge(4).effectValue); } @@ -165,14 +162,12 @@ function applyNDPowers(mult, tier) { InfinityUpgrade.thisInfinityTimeMult.chargedEffect, AlchemyResource.power, Achievement(183), - PelleRifts.death + PelleRifts.paradox ); multiplier = multiplier.pow(getAdjustedGlyphEffect("curseddimensions")); - if (V.has(V_UNLOCKS.ND_POW) && !Pelle.isDoomed) { - multiplier = multiplier.pow(V_UNLOCKS.ND_POW.effect()); - } + multiplier = multiplier.pow(VUnlocks.adPow.effectOrDefault(1)); if (PelleStrikes.infinity.hasStrike) { multiplier = multiplier.pow(0.5); @@ -183,6 +178,8 @@ function applyNDPowers(mult, tier) { } function onBuyDimension(tier) { + if (tier === 1) Tutorial.turnOffEffect(TUTORIAL_STATE.DIM1); + if (tier === 2) Tutorial.turnOffEffect(TUTORIAL_STATE.DIM2); Achievement(10 + tier).unlock(); Achievement(23).tryUnlock(); @@ -464,7 +461,7 @@ class AntimatterDimensionState extends DimensionState { */ get continuumValue() { if (!this.isAvailableForPurchase) return 0; - // Enslaved limits dim 8 purchases to 1 only + // Nameless limits dim 8 purchases to 1 only // Continuum should be no different if (this.tier === 8 && Enslaved.isRunning) return 1; return this.costScale.getContinuumValue(Currency.antimatter.value, 10) * Laitela.matterExtraPurchaseFactor; @@ -568,6 +565,7 @@ class AntimatterDimensionState extends DimensionState { get productionPerSecond() { const tier = this.tier; + if (Laitela.isRunning && tier > Laitela.maxAllowedDimension) return DC.D0; let amount = this.totalAmount; if (NormalChallenge(12).isRunning) { if (tier === 2) amount = amount.pow(1.6); diff --git a/javascripts/core/dimensions/infinity-dimension.js b/javascripts/core/dimensions/infinity-dimension.js index b626d9cc4..e1465f37f 100644 --- a/javascripts/core/dimensions/infinity-dimension.js +++ b/javascripts/core/dimensions/infinity-dimension.js @@ -1,5 +1,6 @@ -import { DimensionState } from "./dimension.js"; -import { DC } from "../constants.js"; +import { DC } from "../constants"; + +import { DimensionState } from "./dimension"; export function infinityDimensionCommonMultiplier() { let mult = new Decimal(ShopPurchase.allDimPurchases.currentMult) @@ -17,12 +18,9 @@ export function infinityDimensionCommonMultiplier() { EternityUpgrade.idMultICRecords, AlchemyResource.dimensionality, ImaginaryUpgrade(8), - PelleRifts.war.milestones[1] + PelleRifts.recursion.milestones[1] ); - - mult = mult.times(NG.multiplier); - if (Replicanti.areUnlocked && Replicanti.amount.gt(1)) { mult = mult.times(replicantiMult()); } @@ -99,7 +97,7 @@ class InfinityDimensionState extends DimensionState { } get canUnlock() { - return ((Perk.bypassIDAntimatter.isBought && !Pelle.isDoomed) || this.antimatterRequirementReached) && + return (Perk.bypassIDAntimatter.canBeApplied || this.antimatterRequirementReached) && this.ipRequirementReached; } @@ -127,6 +125,10 @@ class InfinityDimensionState extends DimensionState { } get productionPerSecond() { + if (EternityChallenge(2).isRunning || EternityChallenge(10).isRunning || + (Laitela.isRunning && this.tier > Laitela.maxAllowedDimension)) { + return DC.D0; + } let production = this.amount; if (EternityChallenge(11).isRunning) { return production; @@ -139,11 +141,6 @@ class InfinityDimensionState extends DimensionState { get multiplier() { const tier = this.tier; - - if (EternityChallenge(2).isRunning || EternityChallenge(10).isRunning || - (Laitela.isRunning && this.tier > Laitela.maxAllowedDimension)) { - return DC.D0; - } if (EternityChallenge(11).isRunning) return DC.D1; let mult = GameCache.infinityDimensionCommonMultiplier.value .timesEffectsOf( @@ -155,18 +152,16 @@ class InfinityDimensionState extends DimensionState { if (tier === 1) { - mult = mult.times(PelleRifts.pestilence.milestones[0].effectOrDefault(1)); + mult = mult.times(PelleRifts.decay.milestones[0].effectOrDefault(1)); } - mult = mult.pow(NG.power); - mult = mult.pow(getAdjustedGlyphEffect("infinitypow")); mult = mult.pow(getAdjustedGlyphEffect("effarigdimensions")); mult = mult.pow(getAdjustedGlyphEffect("curseddimensions")); mult = mult.powEffectOf(AlchemyResource.infinity); mult = mult.pow(Ra.momentumValue); - mult = mult.powEffectOf(PelleRifts.death); + mult = mult.powEffectOf(PelleRifts.paradox); if (player.dilation.active || PelleStrikes.dilation.hasStrike) { mult = dilatedValueOf(mult); @@ -388,7 +383,7 @@ export const InfinityDimensions = { }, get powerConversionRate() { - const multiplier = PelleRifts.death.milestones[2].effectOrDefault(1); + const multiplier = PelleRifts.paradox.milestones[2].effectOrDefault(1); return (7 + getAdjustedGlyphEffect("infinityrate") + PelleUpgrade.infConversion.effectOrDefault(0)) * multiplier; } }; diff --git a/javascripts/core/dimensions/time-dimension.js b/javascripts/core/dimensions/time-dimension.js index 1571006b3..c94501e9c 100644 --- a/javascripts/core/dimensions/time-dimension.js +++ b/javascripts/core/dimensions/time-dimension.js @@ -1,5 +1,6 @@ -import { DimensionState } from "./dimension.js"; -import { DC } from "../constants.js"; +import { DC } from "../constants"; + +import { DimensionState } from "./dimension"; export function buySingleTimeDimension(tier) { const dim = TimeDimension(tier); @@ -102,9 +103,6 @@ export function timeDimensionCommonMultiplier() { PelleRifts.chaos ); - - mult = mult.times(NG.multiplier); - if (EternityChallenge(9).isRunning) { mult = mult.times( Decimal.pow( @@ -146,7 +144,7 @@ class TimeDimensionState extends DimensionState { nextCost(bought) { if (this._tier > 4 && bought < this.e6000ScalingAmount) { const cost = Decimal.pow(this.costMultiplier, bought).times(this.baseCost); - if (PelleRifts.death.milestones[0].canBeApplied) { + if (PelleRifts.paradox.milestones[0].canBeApplied) { return cost.div("1e2250").pow(0.5); } return cost; @@ -163,7 +161,7 @@ class TimeDimensionState extends DimensionState { const exponent = this.e6000ScalingAmount + (bought - this.e6000ScalingAmount) * TimeDimensions.scalingPast1e6000; const cost = Decimal.pow(base, exponent).times(this.baseCost); - if (PelleRifts.death.milestones[0].canBeApplied && this._tier > 4) { + if (PelleRifts.paradox.milestones[0].canBeApplied && this._tier > 4) { return cost.div("1e2250").pow(0.5); } return cost; @@ -184,11 +182,6 @@ class TimeDimensionState extends DimensionState { get multiplier() { const tier = this._tier; - if (EternityChallenge(1).isRunning || EternityChallenge(10).isRunning || - (Laitela.isRunning && tier > Laitela.maxAllowedDimension)) { - return DC.D0; - } - if (EternityChallenge(11).isRunning) return DC.D1; let mult = GameCache.timeDimensionCommonMultiplier.value .timesEffectsOf( @@ -207,9 +200,7 @@ class TimeDimensionState extends DimensionState { mult = mult.powEffectOf(AlchemyResource.time); mult = mult.pow(Ra.momentumValue); mult = mult.pow(ImaginaryUpgrade(11).effectOrDefault(1)); - mult = mult.powEffectOf(PelleRifts.death); - - mult = mult.pow(NG.power); + mult = mult.powEffectOf(PelleRifts.paradox); if (player.dilation.active || PelleStrikes.dilation.hasStrike) { mult = dilatedValueOf(mult); @@ -225,6 +216,10 @@ class TimeDimensionState extends DimensionState { } get productionPerSecond() { + if (EternityChallenge(1).isRunning || EternityChallenge(10).isRunning || + (Laitela.isRunning && this.tier > Laitela.maxAllowedDimension)) { + return DC.D0; + } if (EternityChallenge(11).isRunning) { return this.amount; } diff --git a/javascripts/core/eternity.js b/javascripts/core/eternity.js index cd3e0d2cf..75683a2da 100644 --- a/javascripts/core/eternity.js +++ b/javascripts/core/eternity.js @@ -1,5 +1,6 @@ -import { GameMechanicState, SetPurchasableMechanicState } from "./game-mechanics/index.js"; -import { DC } from "./constants.js"; +import { GameMechanicState, SetPurchasableMechanicState } from "./game-mechanics/index"; +import { DC } from "./constants"; +import FullScreenAnimationHandler from "./full-screen-animation-handler"; function giveEternityRewards(auto) { player.records.bestEternity.time = Math.min(player.records.thisEternity.time, player.records.bestEternity.time); @@ -7,7 +8,7 @@ function giveEternityRewards(auto) { const newEternities = Pelle.isDisabled("eternityMults") ? new Decimal(1) - : new Decimal(RealityUpgrade(3).effectOrDefault(1)).times(getAdjustedGlyphEffect("timeetermult")); + : new Decimal(getAdjustedGlyphEffect("timeetermult")).timesEffectsOf(RealityUpgrade(3), Achievement(113)); if (Currency.eternities.eq(0) && newEternities.lte(10)) { Tab.dimensions.time.show(); @@ -24,17 +25,16 @@ function giveEternityRewards(auto) { if (EternityChallenge.isRunning) { const challenge = EternityChallenge.current; - challenge.addCompletion(); + challenge.addCompletion(false); if (Perk.studyECBulk.isBought) { let completionCount = 0; while (!challenge.isFullyCompleted && challenge.canBeCompleted) { - challenge.addCompletion(); + challenge.addCompletion(false); completionCount++; } AutomatorData.lastECCompletionCount = completionCount; if (Enslaved.isRunning && completionCount > 5) EnslavedProgress.ec1.giveProgress(); } - // eslint-disable-next-line no-bitwise player.challenge.eternity.requirementBits &= ~(1 << challenge.id); respecTimeStudies(auto); } @@ -57,14 +57,12 @@ function giveEternityRewards(auto) { } export function eternityAnimation() { - document.body.style.animation = "eternify 3s 1"; - setTimeout(() => { - document.body.style.animation = ""; - }, 3000); + FullScreenAnimationHandler.display("a-eternify", 3); } export function eternityResetRequest() { if (!Player.canEternity) return; + if (GameEnd.creditsEverClosed) return; askEternityConfirmation(); } @@ -73,6 +71,11 @@ export function eternity(force, auto, specialConditions = {}) { // eslint-disable-next-line no-param-reassign force = true; } + // We define this variable so we can use it in checking whether to give + // the secret achievement for respec without studies. + // Annoyingly, we need to check for studies right here; giveEternityRewards removes studies if we're in an EC, + // so doing the check later doesn't give us the initial state of having studies or not. + const noStudies = player.timestudy.studies.length === 0; if (force) { player.challenge.eternity.current = 0; } else { @@ -82,20 +85,18 @@ export function eternity(force, auto, specialConditions = {}) { player.requirementChecks.reality.noEternities = false; } - if (player.dilation.active && (!force || Currency.infinityPoints.gte(Number.MAX_VALUE))) { - rewardTP(); - } + if (player.dilation.active && (!force || Currency.infinityPoints.gte(Number.MAX_VALUE))) rewardTP(); initializeChallengeCompletions(); initializeResourcesAfterEternity(); - if (!EternityMilestone.keepAutobuyers.isReached) { + if (!EternityMilestone.keepAutobuyers.isReached && !(Pelle.isDoomed && PelleUpgrade.keepAutobuyers.canBeApplied)) { // Fix infinity because it can only break after big crunch autobuyer interval is maxed player.break = false; } player.challenge.eternity.current = 0; - if (!specialConditions.enteringEC) { + if (!specialConditions.enteringEC && !Pelle.isDoomed) { player.dilation.active = false; } resetInfinityRuns(); @@ -105,6 +106,9 @@ export function eternity(force, auto, specialConditions = {}) { AntimatterDimensions.reset(); if (!specialConditions.enteringEC && player.respec) { + if (noStudies) { + SecretAchievement(34).unlock(); + } respecTimeStudies(auto); player.respec = false; } @@ -132,6 +136,24 @@ export function eternity(force, auto, specialConditions = {}) { return true; } +export function animateAndEternity() { + if (!Player.canEternity) return; + const hasAnimation = !FullScreenAnimationHandler.isDisplaying && + ((player.dilation.active && player.options.animations.dilation) || + (!player.dilation.active && player.options.animations.eternity)); + + if (hasAnimation) { + if (player.dilation.active) { + animateAndUndilate(); + } else { + eternityAnimation(); + setTimeout(eternity, 2250); + } + } else { + eternity(); + } +} + export function initializeChallengeCompletions(isReality) { NormalChallenges.clearCompletions(); if (!PelleUpgrade.keepInfinityChallenges.canBeApplied) InfinityChallenges.clearCompletions(); @@ -167,20 +189,18 @@ export function initializeResourcesAfterEternity() { } function applyRealityUpgradesAfterEternity() { - if (Pelle.isDoomed) return; - if (player.eternityUpgrades.size < 3 && Perk.autounlockEU1.isBought) { + if (player.eternityUpgrades.size < 3 && Perk.autounlockEU1.canBeApplied) { for (const id of [1, 2, 3]) player.eternityUpgrades.add(id); } } function askEternityConfirmation() { - if (player.options.confirmations.eternity) { + if (player.dilation.active && player.options.confirmations.dilation) { + Modal.exitDilation.show(); + } else if (player.options.confirmations.eternity) { Modal.eternity.show(); - } else if (player.options.animations.eternity && document.body.style.animation === "") { - eternityAnimation(); - setTimeout(eternity, 2250); } else { - eternity(); + animateAndEternity(); } } @@ -190,50 +210,18 @@ export class EternityMilestoneState { } get isReached() { - if (Pelle.isDoomed && this.config.pelleObsolete) { - return this.config.pelleObsolete(); + if (Pelle.isDoomed && this.config.givenByPelle) { + return this.config.givenByPelle(); } return Currency.eternities.gte(this.config.eternities); } } - -export const EternityMilestone = (function() { - const db = GameDatabase.eternity.milestones; - const infinityDims = Array.dimensionTiers - .map(tier => new EternityMilestoneState(db[`autobuyerID${tier}`])); - return { - autobuyerIPMult: new EternityMilestoneState(db.autobuyerIPMult), - keepAutobuyers: new EternityMilestoneState(db.keepAutobuyers), - autobuyerReplicantiGalaxy: new EternityMilestoneState(db.autobuyerReplicantiGalaxy), - keepInfinityUpgrades: new EternityMilestoneState(db.keepInfinityUpgrades), - bigCrunchModes: new EternityMilestoneState(db.bigCrunchModes), - autoEP: new EternityMilestoneState(db.autoEP), - autoIC: new EternityMilestoneState(db.autoIC), - autobuyMaxGalaxies: new EternityMilestoneState(db.autobuyMaxGalaxies), - unlockReplicanti: new EternityMilestoneState(db.unlockReplicanti), - autobuyerID: tier => infinityDims[tier - 1], - keepBreakUpgrades: new EternityMilestoneState(db.keepBreakUpgrades), - autoUnlockID: new EternityMilestoneState(db.autoUnlockID), - unlockAllND: new EternityMilestoneState(db.unlockAllND), - replicantiNoReset: new EternityMilestoneState(db.replicantiNoReset), - autobuyerReplicantiChance: new EternityMilestoneState(db.autobuyerReplicantiChance), - autobuyerReplicantiInterval: new EternityMilestoneState(db.autobuyerReplicantiInterval), - autobuyerReplicantiMaxGalaxies: new EternityMilestoneState(db.autobuyerReplicantiMaxGalaxies), - autobuyerEternity: new EternityMilestoneState(db.autobuyerEternity), - autoEternities: new EternityMilestoneState(db.autoEternities), - autoInfinities: new EternityMilestoneState(db.autoInfinities), - }; -}()); - -export const EternityMilestones = { - // This is a bit of a hack because autobuyerID is a function that returns EternityMilestoneState objects instead of a - // EternityMilestoneState object itself - all: Object.values(EternityMilestone) - .filter(m => typeof m !== "function") - .concat(Array.dimensionTiers - .map(tier => new EternityMilestoneState(GameDatabase.eternity.milestones[`autobuyerID${tier}`])) - ) -}; +export const EternityMilestone = mapGameDataToObject( + GameDatabase.eternity.milestones, + config => (config.isBaseResource + ? new EternityMilestoneState(config) + : new EternityMilestoneState(config)) +); class EternityUpgradeState extends SetPurchasableMechanicState { get currency() { @@ -321,16 +309,9 @@ class EPMultiplierState extends GameMechanicState { } } +export const EternityUpgrade = mapGameDataToObject( + GameDatabase.eternity.upgrades, + config => new EternityUpgradeState(config) +); -export const EternityUpgrade = (function() { - const db = GameDatabase.eternity.upgrades; - return { - idMultEP: new EternityUpgradeState(db.idMultEP), - idMultEternities: new EternityUpgradeState(db.idMultEternities), - idMultICRecords: new EternityUpgradeState(db.idMultICRecords), - tdMultAchs: new EternityUpgradeState(db.tdMultAchs), - tdMultTheorems: new EternityUpgradeState(db.tdMultTheorems), - tdMultRealTime: new EternityUpgradeState(db.tdMultRealTime), - epMult: new EPMultiplierState(), - }; -}()); +EternityUpgrade.epMult = new EPMultiplierState(); diff --git a/javascripts/core/eternity_challenge.js b/javascripts/core/eternity_challenge.js index 303386ec5..b74ce4324 100644 --- a/javascripts/core/eternity_challenge.js +++ b/javascripts/core/eternity_challenge.js @@ -1,6 +1,6 @@ -import { GameMechanicState } from "./game-mechanics/index.js"; -import { DC } from "./constants.js"; +import { DC } from "./constants"; import { deepmergeAll } from "@/utility/deepmerge"; +import { GameMechanicState } from "./game-mechanics/index"; export function startEternityChallenge() { initializeChallengeCompletions(); @@ -158,8 +158,11 @@ export class EternityChallengeState extends GameMechanicState { return Math.min(Math.floor(completions), this.maxCompletions); } - addCompletion() { + addCompletion(auto = false) { this.completions++; + if ((this.id === 4 || this.id === 12) && auto) { + this.tryFail(true); + } if (this.id === 6) { GameCache.dimensionMultDecrease.invalidate(); } @@ -170,6 +173,7 @@ export class EternityChallengeState extends GameMechanicState { requestStart() { if (!Tab.challenges.eternity.isUnlocked || this.isRunning) return; + if (GameEnd.creditsEverClosed) return; if (!player.options.confirmations.challenges) { this.start(); return; @@ -194,7 +198,7 @@ export class EternityChallengeState extends GameMechanicState { } if (Enslaved.isRunning) { if (this.id === 6 && this.completions === 5) EnslavedProgress.ec6.giveProgress(); - if (EnslavedProgress.challengeCombo.hasProgress) Tab.challenges.normal.show(); + if (!auto && EnslavedProgress.challengeCombo.hasProgress) Tab.challenges.normal.show(); } startEternityChallenge(); return true; @@ -224,22 +228,37 @@ export class EternityChallengeState extends GameMechanicState { eternity(true); } - fail() { + fail(auto = false) { this.exit(); let reason; - if (this.id === 4) { - reason = restriction => `having more than ${quantifyInt("Infinity", restriction)}`; + if (auto) { + if (this.id === 4) { + reason = restriction => `Auto Eternity Challenge completion completed ` + + `Eternity Challenge ${this.id} and made the next tier ` + + `require having less Infinities (${quantifyInt("Infinity", restriction)} ` + + `or less) than you had`; + } else if (this.id === 12) { + reason = restriction => `Auto Eternity Challenge completion completed ` + + `Eternity Challenge ${this.id} and made the next tier ` + + `require spending less time in it (${quantify("in-game second", restriction, 0, 1)} ` + + `or less) than you had spent`; + } + } else if (this.id === 4) { + reason = restriction => `You failed Eternity Challenge ${this.id} due to ` + + `having more than ${quantifyInt("Infinity", restriction)}`; } else if (this.id === 12) { - reason = restriction => `spending more than ${quantify("in-game second", restriction, 0, 1)} in it`; + reason = restriction => `You failed Eternity Challenge ${this.id} due to ` + + `spending more than ${quantify("in-game second", restriction, 0, 1)} in it`; } - Modal.message.show(`You failed Eternity Challenge ${this.id} due to - ${reason(this.config.restriction(this.completions))}; you have now exited it.`); + Modal.message.show(`${reason(this.config.restriction(this.completions))}, ` + + `which has caused you to exit it.`, + { closeEvent: GAME_EVENT.REALITY_RESET_AFTER }, 1); EventHub.dispatch(GAME_EVENT.CHALLENGE_FAILED); } - tryFail() { + tryFail(auto = false) { if (this.isRunning && !this.isWithinRestriction) { - this.fail(); + this.fail(auto); return true; } return false; @@ -292,11 +311,11 @@ export const EternityChallenges = { autoComplete: { tick() { if (!player.reality.autoEC || Pelle.isDisabled("autoec")) return; - if (Ra.has(RA_UNLOCKS.AUTO_RU_AND_INSTANT_EC)) { + if (Ra.unlocks.instantECAndRealityUpgradeAutobuyers.canBeApplied) { let next = this.nextChallenge; while (next !== undefined) { while (!next.isFullyCompleted) { - next.addCompletion(); + next.addCompletion(true); } next = this.nextChallenge; } @@ -306,7 +325,7 @@ export const EternityChallenges = { let next = this.nextChallenge; while (player.reality.lastAutoEC - interval > 0 && next !== undefined) { player.reality.lastAutoEC -= interval; - next.addCompletion(); + next.addCompletion(true); next = this.nextChallenge; } player.reality.lastAutoEC %= interval; @@ -317,14 +336,14 @@ export const EternityChallenges = { }, get interval() { - if (!Perk.autocompleteEC1.isBought || Pelle.isDisabled("autoec")) return Infinity; + if (!Perk.autocompleteEC1.canBeApplied) return Infinity; let minutes = Effects.min( Number.MAX_VALUE, Perk.autocompleteEC1, Perk.autocompleteEC2, Perk.autocompleteEC3 ); - if (V.has(V_UNLOCKS.FAST_AUTO_EC)) minutes /= V_UNLOCKS.FAST_AUTO_EC.effect(); + minutes /= VUnlocks.fastAutoEC.effectOrDefault(1); return TimeSpan.fromMinutes(minutes).totalMilliseconds; } } diff --git a/javascripts/core/event-hub.js b/javascripts/core/event-hub.js index 1ad44068f..3c0619c24 100644 --- a/javascripts/core/event-hub.js +++ b/javascripts/core/event-hub.js @@ -19,7 +19,6 @@ window.EventHub = class EventHub { } } - // eslint-disable-next-line max-params dispatch(event, args) { const handlers = this._handlers[event]; if (handlers === undefined) return; @@ -28,7 +27,6 @@ window.EventHub = class EventHub { } } - // eslint-disable-next-line max-params static dispatch(event, ...args) { EventHub.logic.dispatch(event, args); GameUI.dispatch(event, args); @@ -78,8 +76,8 @@ window.GAME_EVENT = { GLYPHS_EQUIPPED_CHANGED: "GLYPHS_EQUIPPED_CHANGED", GLYPHS_CHANGED: "GLYPHS_CHANGED", GLYPH_SACRIFICED: "GLYPH_SACRIFICED", - GLYPH_CHOICES_GENERATED: "GLYPH_CHOICES_GENERATED", GLYPH_SET_SAVE_CHANGE: "GLYPH_SET_SAVE_CHANGE", + GLYPH_VISUAL_CHANGE: "GLYPH_VISUAL_CHANGE", // Break Infinity BREAK_INFINITY: "BREAK_INFINITY", @@ -89,14 +87,17 @@ window.GAME_EVENT = { INFINITY_DIMENSION_UNLOCKED: "INFINITY_DIMENSION_UNLOCKED", INFINITY_CHALLENGE_COMPLETED: "INFINITY_CHALLENGE_COMPLETED", INFINITY_UPGRADE_BOUGHT: "INFINITY_UPGRADE_BOUGHT", + INFINITY_UPGRADE_CHARGED: "INFINITY_UPGRADE_CHARGED", + INFINITY_UPGRADES_DISCHARGED: "INFINITY_UPGRADES_DISCHARGED", ACHIEVEMENT_UNLOCKED: "ACHIEVEMENT_UNLOCKED", CHALLENGE_FAILED: "CHALLENGE_FAILED", REALITY_UPGRADE_BOUGHT: "REALITY_UPGRADE_BOUGHT", REALITY_UPGRADE_TEN_BOUGHT: "REALITY_UPGRADE_TEN_BOUGHT", PERK_BOUGHT: "PERK_BOUGHT", + BLACK_HOLE_UNLOCKED: "BLACK_HOLE_UNLOCKED", BLACK_HOLE_UPGRADE_BOUGHT: "BLACK_HOLE_UPGRADE_BOUGHT", GAME_LOAD: "GAME_LOAD", - CELESTIAL_UPGRADE_UNLOCKED: "CELESTIAL_UPGRADE_UNLOCKED", + OFFLINE_CURRENCY_GAINED: "OFFLINE_CURRENCY_GAINED", SAVE_CONVERTED_FROM_PREVIOUS_VERSION: "SAVE_CONVERTED_FROM_PREVIOUS_VERSION", REALITY_FIRST_UNLOCKED: "REALITY_FIRST_UNLOCKED", AUTOMATOR_SAVE_CHANGED: "AUTOMATOR_SAVE_CHANGED", @@ -107,6 +108,7 @@ window.GAME_EVENT = { ACHIEVEMENT_EVENT_OTHER: "ACHIEVEMENT_EVENT_OTHER", ENTER_PRESSED: "ENTER_PRESSED", + ARROW_KEY_PRESSED: "ARROW_KEY_PRESSED", // UI Events UPDATE: "UPDATE", diff --git a/javascripts/core/extensions.js b/javascripts/core/extensions.js index c35e975ba..a10e94c46 100644 --- a/javascripts/core/extensions.js +++ b/javascripts/core/extensions.js @@ -265,13 +265,11 @@ Array.prototype.compact = function() { }; Array.prototype.toBitmask = function() { - // eslint-disable-next-line no-bitwise return this.reduce((prev, val) => prev | (1 << val), 0); }; Set.prototype.toBitmask = function() { let mask = 0; - // eslint-disable-next-line no-bitwise for (const id of this) mask |= (1 << id); return mask; }; @@ -280,9 +278,8 @@ Array.fromBitmask = function(mask) { const bitIndices = []; let currentIndex = 0; while (mask !== 0) { - // eslint-disable-next-line no-bitwise if (mask & 1) bitIndices.push(currentIndex); - // eslint-disable-next-line no-bitwise, no-param-reassign + // eslint-disable-next-line no-param-reassign mask >>= 1; ++currentIndex; } diff --git a/javascripts/core/format.js b/javascripts/core/format.js index 985c260ba..baee70c85 100644 --- a/javascripts/core/format.js +++ b/javascripts/core/format.js @@ -1,18 +1,26 @@ -window.format = function format(value, places, placesUnder1000) { - if (Pelle.isDoomed) { - if ((Pelle.endState - 2.5) / 2 > Math.random()) return "END"; - } - return Notations.current.format(value, places, placesUnder1000); +function isEND() { + const threshold = GameEnd.endState > END_STATE_MARKERS.END_NUMBERS + ? 1 + : (GameEnd.endState - END_STATE_MARKERS.FADE_AWAY) / 2; + // Using the Pelle.isDoomed getter here causes this to not update properly after a game restart + return player.celestials.pelle.doomed && Math.random() < threshold; +} + +window.format = function format(value, places = 0, placesUnder1000 = 0) { + if (isEND()) return "END"; + return Notations.current.format(value, places, placesUnder1000, 3); }; window.formatInt = function formatInt(value) { + if (isEND()) return "END"; if (Notations.current.isPainful) { - return format(value, 2, 0); + return format(value, 2); } return formatWithCommas(typeof value === "number" ? value.toFixed(0) : value.toNumber().toFixed(0)); }; window.formatFloat = function formatFloat(value, digits) { + if (isEND()) return "END"; if (Notations.current.isPainful) { return format(value, Math.max(2, digits), digits); } @@ -20,6 +28,7 @@ window.formatFloat = function formatFloat(value, digits) { }; window.formatPostBreak = function formatPostBreak(value, places, placesUnder1000) { + if (isEND()) return "END"; const notation = Notations.current; // This is basically just a copy of the format method from notations library, // with the pre-break case removed. @@ -66,11 +75,15 @@ window.formatRarity = function formatRarity(value) { return `${format(value, 2, places)}%`; }; -// We assume 2/2 decimal places to keep parameter count sensible; this is used very rarely +// We assume 2/0, 2/2 decimal places to keep parameter count sensible; this is used very rarely window.formatMachines = function formatMachines(realPart, imagPart) { + if (isEND()) return "END"; const parts = []; - if (Decimal.neq(realPart, 0)) parts.push(format(realPart, 2, 2)); + if (Decimal.neq(realPart, 0)) parts.push(format(realPart, 2)); if (Decimal.neq(imagPart, 0)) parts.push(`${format(imagPart, 2, 2)}i`); + // This function is used for just RM and just iM in a few spots, so we have to push both parts conditionally + // Nonetheless, we also need to special-case both zero so that it doesn't end up displaying as an empty string + if (Decimal.eq(realPart, 0) && Decimal.eq(imagPart, 0)) return format(0); return parts.join(" + "); }; diff --git a/javascripts/core/full-screen-animation-handler.js b/javascripts/core/full-screen-animation-handler.js new file mode 100644 index 000000000..1f21f73ec --- /dev/null +++ b/javascripts/core/full-screen-animation-handler.js @@ -0,0 +1,16 @@ +export default { + isDisplaying: false, + displayForce(name, duration) { + document.body.style.animation = `${name} ${duration}s 1`; + this.isDisplaying = true; + setTimeout(() => { + document.body.style.animation = ""; + this.isDisplaying = false; + }, duration * 1000); + }, + display(name, duration) { + if (!this.isDisplaying) { + this.displayForce(name, duration); + } + } +}; \ No newline at end of file diff --git a/javascripts/core/galaxy.js b/javascripts/core/galaxy.js index 738cbfc75..418b529ad 100644 --- a/javascripts/core/galaxy.js +++ b/javascripts/core/galaxy.js @@ -121,7 +121,9 @@ export class Galaxy { function galaxyReset() { EventHub.dispatch(GAME_EVENT.GALAXY_RESET_BEFORE); player.galaxies++; - if (!Achievement(143).isUnlocked) player.dimensionBoosts = 0; + if (!Achievement(143).isUnlocked || (Pelle.isDoomed && !PelleUpgrade.galaxyNoResetDimboost.canBeApplied)) { + player.dimensionBoosts = 0; + } softReset(0); if (Notations.current === Notation.emoji) player.requirementChecks.permanent.emojiGalaxies++; // This is specifically reset here because the check is actually per-galaxy and not per-infinity @@ -129,6 +131,16 @@ function galaxyReset() { EventHub.dispatch(GAME_EVENT.GALAXY_RESET_AFTER); } +export function manualRequestGalaxyReset(bulk) { + if (!Galaxy.canBeBought || !Galaxy.requirement.isSatisfied) return; + if (GameEnd.creditsEverClosed) return; + if (player.options.confirmations.antimatterGalaxy) { + Modal.antimatterGalaxy.show({ bulk }); + return; + } + requestGalaxyReset(bulk); +} + export function requestGalaxyReset(bulk, limit = Number.MAX_VALUE) { if (EternityMilestone.autobuyMaxGalaxies.isReached && bulk) return maxBuyGalaxies(limit); if (player.galaxies >= limit || !Galaxy.canBeBought || !Galaxy.requirement.isSatisfied) return false; diff --git a/javascripts/core/game-mechanics/bit-purchasable.js b/javascripts/core/game-mechanics/bit-purchasable.js index 670998158..65610bede 100644 --- a/javascripts/core/game-mechanics/bit-purchasable.js +++ b/javascripts/core/game-mechanics/bit-purchasable.js @@ -1,4 +1,4 @@ -import { PurchasableMechanicState } from "./puchasable.js"; +import { PurchasableMechanicState } from "./puchasable"; /** * @abstract @@ -20,16 +20,13 @@ export class BitPurchasableMechanicState extends PurchasableMechanicState { get bitIndex() { throw new NotImplementedError(); } get isBought() { - // eslint-disable-next-line no-bitwise return (this.bits & (1 << this.bitIndex)) !== 0; } set isBought(value) { if (value) { - // eslint-disable-next-line no-bitwise this.bits |= (1 << this.bitIndex); } else { - // eslint-disable-next-line no-bitwise this.bits &= ~(1 << this.bitIndex); } } diff --git a/javascripts/core/game-mechanics/bit-upgrade-state.js b/javascripts/core/game-mechanics/bit-upgrade-state.js new file mode 100644 index 000000000..66a42e0ee --- /dev/null +++ b/javascripts/core/game-mechanics/bit-upgrade-state.js @@ -0,0 +1,38 @@ +import { GameMechanicState } from "./game-mechanic"; + +/** + * @abstract + */ +export class BitUpgradeState extends GameMechanicState { + constructor(config) { + super(config); + if (this.id < 0 || this.id > 31) throw new Error(`Id ${this.id} out of bit range`); + } + + /** + * @abstract + */ + get bits() { throw new NotImplementedError(); } + set bits(value) { throw new NotImplementedError(); } + + get isUnlocked() { + return Boolean(this.bits & (1 << this.id)); + } + + get canBeApplied() { + return this.isUnlocked && this.isEffectActive; + } + + get canBeUnlocked() { + return !this.isUnlocked; + } + + // eslint-disable-next-line no-empty-function + onUnlock() { } + + unlock() { + if (!this.canBeUnlocked) return; + this.bits |= (1 << this.id); + this.onUnlock(); + } +} diff --git a/javascripts/core/game-mechanics/game-mechanic.js b/javascripts/core/game-mechanics/game-mechanic.js index 0b76a7e62..03e156de6 100644 --- a/javascripts/core/game-mechanics/game-mechanic.js +++ b/javascripts/core/game-mechanics/game-mechanic.js @@ -1,4 +1,4 @@ -import { Effect } from "./effect.js"; +import { Effect } from "./effect"; /** * @abstract @@ -15,7 +15,7 @@ export class GameMechanicState extends Effect { for (const key in config.effects) { const nested = config.effects[key]; let effect; - if (typeof nested === "number" || nested instanceof Decimal) { + if (typeof nested === "number" || typeof nested === "function" || nested instanceof Decimal) { effect = new Effect(nested); } else { effect = new Effect(nested.effect, nested.cap, nested.effectCondition); diff --git a/javascripts/core/game-mechanics/index.js b/javascripts/core/game-mechanics/index.js index 3544ea6bf..9756ec589 100644 --- a/javascripts/core/game-mechanics/index.js +++ b/javascripts/core/game-mechanics/index.js @@ -1,7 +1,8 @@ -export * from "./effect.js"; -export * from "./effects.js"; -export * from "./game-mechanic.js"; -export * from "./puchasable.js"; -export * from "./set-purchasable.js"; -export * from "./bit-purchasable.js"; -export * from "./rebuyable.js"; +export * from "./effect"; +export * from "./effects"; +export * from "./game-mechanic"; +export * from "./bit-upgrade-state"; +export * from "./puchasable"; +export * from "./set-purchasable"; +export * from "./bit-purchasable"; +export * from "./rebuyable"; diff --git a/javascripts/core/game-mechanics/puchasable.js b/javascripts/core/game-mechanics/puchasable.js index fb4843bb4..44f24c146 100644 --- a/javascripts/core/game-mechanics/puchasable.js +++ b/javascripts/core/game-mechanics/puchasable.js @@ -1,4 +1,4 @@ -import { GameMechanicState } from "./game-mechanic.js"; +import { GameMechanicState } from "./game-mechanic"; /** * @abstract diff --git a/javascripts/core/game-mechanics/rebuyable.js b/javascripts/core/game-mechanics/rebuyable.js index f6c98eab0..ba0f377f8 100644 --- a/javascripts/core/game-mechanics/rebuyable.js +++ b/javascripts/core/game-mechanics/rebuyable.js @@ -1,4 +1,4 @@ -import { GameMechanicState } from "./game-mechanic.js"; +import { GameMechanicState } from "./game-mechanic"; /** * @abstract @@ -49,6 +49,7 @@ export class RebuyableMechanicState extends GameMechanicState { purchase() { if (!this.canBeBought) return false; + if (GameEnd.creditsEverClosed) return false; this.currency.subtract(this.cost); this.boughtAmount++; this.onPurchased(); diff --git a/javascripts/core/game-mechanics/set-purchasable.js b/javascripts/core/game-mechanics/set-purchasable.js index 0a763198c..96e48ca6d 100644 --- a/javascripts/core/game-mechanics/set-purchasable.js +++ b/javascripts/core/game-mechanics/set-purchasable.js @@ -1,4 +1,4 @@ -import { PurchasableMechanicState } from "./puchasable.js"; +import { PurchasableMechanicState } from "./puchasable"; /** * @abstract diff --git a/javascripts/core/globals.js b/javascripts/core/globals.js index 3563d6bcd..bdfa2fe6b 100644 --- a/javascripts/core/globals.js +++ b/javascripts/core/globals.js @@ -1,89 +1,93 @@ -export * from "./glyph-effects.js"; -export * from "./player.js"; +export * from "./glyph-effects"; +export * from "./player"; -export * from "./automator/automator-backend.js"; -export * from "./performance-stats.js"; -export * from "./currency.js"; -export * from "./cache.js"; -export * from "./intervals.js"; -export * from "./keyboard.js"; -export * from "./hotkeys.js"; -export * from "./galaxy.js"; -export * from "./away-progress.js"; -export * from "./confirmations.js"; +export * from "./automator/automator-backend"; +export * from "./performance-stats"; +export * from "./currency"; +export * from "./cache"; +export * from "./intervals"; +export * from "./keyboard"; +export * from "./hotkeys"; +export * from "./galaxy"; +export * from "./away-progress"; +export * from "./confirmations"; -export * from "./autobuyers/index.js"; -export * from "./storage/index.js"; +export * from "./autobuyers/index"; +export * from "./storage/index"; -export * from "./notations.js"; -export * from "./tutorial.js"; +export * from "./notations"; +export * from "./tutorial"; -export * from "./new-game.js"; +export * from "./new-game"; -export * from "./celestials/quotes.js"; -export * from "./celestials/teresa.js"; -export * from "./celestials/effarig.js"; -export * from "./celestials/enslaved.js"; -export * from "./celestials/V.js"; -export * from "./celestials/ra/ra.js"; -export * from "./celestials/ra/alchemy.js"; -export * from "./celestials/laitela/laitela.js"; -export * from "./celestials/laitela/dark-matter-dimension.js"; -export * from "./celestials/laitela/singularity.js"; -export * from "./celestials/pelle/pelle.js"; -export * from "./celestials/pelle/strikes.js"; -export * from "./celestials/pelle/rifts.js"; -export * from "./celestials/pelle/galaxy-generator.js"; -export * from "./celestials/celestials.js"; +export * from "./celestials/quotes"; +export * from "./celestials/teresa"; +export * from "./celestials/effarig"; +export * from "./celestials/enslaved"; +export * from "./celestials/V"; +export * from "./celestials/ra/ra"; +export * from "./celestials/ra/alchemy"; +export * from "./celestials/laitela/laitela"; +export * from "./celestials/laitela/dark-matter-dimension"; +export * from "./celestials/laitela/singularity"; +export * from "./celestials/pelle/pelle"; +export * from "./celestials/pelle/strikes"; +export * from "./celestials/pelle/rifts"; +export * from "./celestials/pelle/galaxy-generator"; +export * from "./celestials/pelle/game-end"; +export * from "./celestials/celestials"; -export * from "./automator/index.js"; -export * from "./automator/automator-points.js"; +export * from "./automator/index"; +export * from "./automator/automator-points"; -export * from "./app/player-progress.js"; -export * from "./app/modal.js"; -export * from "./app/themes.js"; -export * from "./app/options.js"; -export * from "./app/ui.js"; +export * from "./app/player-progress"; +export * from "./app/modal"; +export * from "./app/themes"; +export * from "./app/options"; +export * from "./app/ui"; -export * from "./achievements/normal-achievement.js"; -export * from "./achievements/secret-achievement.js"; -export * from "./achievements/achievement-timer.js"; +export * from "./achievements/normal-achievement"; +export * from "./achievements/secret-achievement"; +export * from "./achievements/achievement-timer"; -export * from "./glyphs/glyph-core.js"; -export * from "./glyphs/glyph-effects.js"; -export * from "./glyphs/glyph-generator.js"; -export * from "./glyphs/glyph-purge-handler.js"; -export * from "./glyphs/auto-glyph-processor.js"; +export * from "./glyphs/glyph-core"; +export * from "./glyphs/glyph-effects"; +export * from "./glyphs/glyph-generator"; +export * from "./glyphs/glyph-purge-handler"; +export * from "./glyphs/auto-glyph-processor"; -export * from "./time.js"; -export * from "./tickspeed.js"; +export * from "./time"; +export * from "./tickspeed"; -export * from "./dimensions/antimatter-dimension.js"; -export * from "./dimensions/infinity-dimension.js"; -export * from "./dimensions/time-dimension.js"; +export * from "./dimensions/antimatter-dimension"; +export * from "./dimensions/infinity-dimension"; +export * from "./dimensions/time-dimension"; -export * from "./time-studies/index.js"; +export * from "./time-studies/index"; -export * from "./dimboost.js"; -export * from "./sacrifice.js"; -export * from "./big_crunch.js"; -export * from "./challenge.js"; -export * from "./eternity.js"; -export * from "./eternity_challenge.js"; -export * from "./reality.js"; -export * from "./replicanti.js"; -export * from "./time-theorems.js"; -export * from "./reality-upgrades.js"; -export * from "./imaginary-upgrades.js"; -export * from "./perks.js"; -export * from "./dilation.js"; -export * from "./black_hole.js"; -export * from "./machines.js"; -export * from "./devtools.js"; -export * from "./news-ticker.js"; -export * from "./kong.js"; -export * from "./ui/tabs.js"; -export * from "./ui/tab-notifications.js"; -export * from "./speedrun.js"; +export * from "./dimboost"; +export * from "./sacrifice"; +export * from "./big_crunch"; +export * from "./infinity-upgrades"; +export * from "./break-infinity-upgrades"; +export * from "./normal-challenges"; +export * from "./infinity-challenges"; +export * from "./eternity"; +export * from "./eternity_challenge"; +export * from "./reality"; +export * from "./replicanti"; +export * from "./time-theorems"; +export * from "./reality-upgrades"; +export * from "./imaginary-upgrades"; +export * from "./perks"; +export * from "./dilation"; +export * from "./black_hole"; +export * from "./machines"; +export * from "./devtools"; +export * from "./news-ticker"; +export * from "./kong"; +export * from "./ui/tabs"; +export * from "./ui/tab-notifications"; +export * from "./speedrun"; -export * from "./automator/script-templates.js"; +export * from "./automator/script-templates"; diff --git a/javascripts/core/glyph-effects.js b/javascripts/core/glyph-effects.js index 717b69d7f..71bdcdd8e 100644 --- a/javascripts/core/glyph-effects.js +++ b/javascripts/core/glyph-effects.js @@ -1,28 +1,4 @@ -import { GameDatabase } from "./secret-formula/game-database.js"; -import { DC } from "./constants.js"; - -// There is a little too much stuff about glyph effects to put in constants. - -// The last glyph type you can only get if you got effarig reality -export const GLYPH_TYPES = ["power", "infinity", "replication", "time", "dilation", "effarig", - "reality", "cursed", "companion"]; -export const BASIC_GLYPH_TYPES = ["power", "infinity", "replication", "time", "dilation"]; -export const ALCHEMY_BASIC_GLYPH_TYPES = ["power", "infinity", "replication", "time", "dilation", "effarig"]; -export const GLYPH_SYMBOLS = { power: "Ω", infinity: "∞", replication: "Ξ", time: "Δ", dilation: "Ψ", - effarig: "Ϙ", reality: "Ϟ", cursed: "⸸", companion: "♥" }; -export const CANCER_GLYPH_SYMBOLS = { power: "⚡", infinity: "8", replication: "⚤", time: "🕟", dilation: "☎", - effarig: "🦒", reality: "⛧", cursed: "☠", companion: "³" }; - -export const GlyphCombiner = Object.freeze({ - add: x => x.reduce(Number.sumReducer, 0), - multiply: x => x.reduce(Number.prodReducer, 1), - // For exponents, the base value is 1, so when we add two exponents a and b we want to get a + b - 1, - // so that if a and b are both close to 1 so is their sum. In general, when we add a list x of exponents, - // we have to add 1 - x.length to the actual sum, so that if all the exponents are close to 1 the result - // is also close to 1 rather than close to x.length. - addExponents: x => x.reduce(Number.sumReducer, 1 - x.length), - multiplyDecimal: x => x.reduce(Decimal.prodReducer, DC.D1) -}); +import { GameDatabase } from "./secret-formula/game-database"; /** * Multiple glyph effects are combined into a summary object of this type. @@ -41,89 +17,117 @@ class GlyphEffectConfig { * glyphs. * @param {string} [setup.genericDesc] (Defaults to singleDesc with {value} replaced with "x") Generic * description of the glyph's effect + * @param {string} [setup.shortDesc] Short and condensed version of the glyph's effect for use in the Modal * @param {(function(number, number): number) | function(number, number): Decimal} [setup.effect] Calculate effect * value from level and strength - * @param {NumericToString} [setup.formatEffect] Format the effect's value into a string. Defaults + * @param {function(number | Decimal): string} [setup.formatEffect] Format the effect's value into a string. Defaults * to format(x, 3, 3) - * @param {NumericToString} [setup.formatSingleEffect] Format the effect's value into a string, used + * @param {function(number | Decimal): string} [setup.formatSingleEffect] Format the effect's value into a string, used * for effects which need to display different values in single values versus combined values (eg. power effects) - * @param {NumericFunction} [setup.softcap] An optional softcap to be applied after glyph + * @param {function(number | Decimal): number | Decimal} [setup.softcap] An optional softcap to be applied after glyph * effects are combined. * @param {((function(number[]): GlyphEffectConfig__combine_result) | function(number[]): number)} setup.combine * Specification of how multiple glyphs combine. Can be GlyphCombiner.add or GlyphCombiner.multiply for most glyphs. * Otherwise, should be a function that takes a potentially empty array of numbers (each glyph's effect value) * and returns a combined effect or an object with the combined effect amd a capped indicator. - * + * @param {boolean} [setup.enabledInDoomed] Determines if this effect is enabled while doomed. Defaults to false */ constructor(setup) { GlyphEffectConfig.checkInputs(setup); - /** @member{string} unique key for the effect -- powerpow, etc */ + /** @type {string} unique key for the effect -- powerpow, etc */ this.id = setup.id; - /** @member{number} bit position for the effect in the effect bitmask */ + /** @type {number} bit position for the effect in the effect bitmask */ this.bitmaskIndex = setup.bitmaskIndex; - /** @member{boolean} flag to separate "basic"/effarig glyphs from cursed/reality glyphs */ + /** @type {boolean} flag to separate "basic"/effarig glyphs from cursed/reality glyphs */ this.isGenerated = setup.isGenerated; - /** @member{string[]} the types of glyphs this effect can occur on */ + /** @type {string[]} the types of glyphs this effect can occur on */ this.glyphTypes = setup.glyphTypes; - /** @member{string} See info about setup, above*/ - this.singleDesc = setup.singleDesc; - /** @member{string} See info about setup, above*/ - this.totalDesc = setup.totalDesc || setup.singleDesc; - /** @member {string} genericDesc description of the effect without a specific value */ - this.genericDesc = setup.genericDesc || setup.singleDesc.replace("{value}", "x"); - /** @member {string} shortDesc shortened description for use in glyph choice info modal */ - this.shortDesc = setup.shortDesc; + /** @type {string} See info about setup, above */ + this._singleDesc = setup.singleDesc; + /** @type {string} See info about setup, above */ + this._totalDesc = setup.totalDesc ?? setup.singleDesc; + /** @type {string} description of the effect without a specific value */ + this._genericDesc = setup.genericDesc ?? setup.singleDesc.replace("{value}", "x"); + /** @type {string} shortened description for use in glyph choice info modal */ + this._shortDesc = setup.shortDesc; /** - * @member {(function(number, number): number) | function(number, number): Decimal} effect Calculate effect + * @type {(function(number, number): number) | function(number, number): Decimal} Calculate effect * value from level and strength */ this.effect = setup.effect; /** - * @member {NumericToString} formatEffect formatting function for the effect + * @type {function(number | Decimal): string} formatting function for the effect * (just the number conversion). Combined with the description strings to make descriptions */ - this.formatEffect = setup.formatEffect || (x => format(x, 3, 3)); - /** @member{NumericToString} See info about setup, above*/ + this.formatEffect = setup.formatEffect ?? (x => format(x, 3, 3)); + /** @type {function(number | Decimal): string} See info about setup, above */ this.formatSingleEffect = setup.formatSingleEffect || this.formatEffect; /** - * @member {function(number[]): GlyphEffectConfig__combine_result} combine Function that combines + * @type {function(number[]): GlyphEffectConfig__combine_result} combine Function that combines * multiple glyph effects into one value (adds up, applies softcaps, etc) */ this.combine = GlyphEffectConfig.setupCombine(setup); - /** @member{function(number)} conversion function to produce altered glyph effect */ + /** @type {function(number)} conversion function to produce altered glyph effect */ this.conversion = setup.conversion; /** - * @member {NumericToString} formatSecondaryEffect formatting function for + * @type {function(number | Decimal): string} formatSecondaryEffect formatting function for * the secondary effect (if there is one) */ this.formatSecondaryEffect = setup.formatSecondaryEffect || (x => format(x, 3, 3)); - /** @member{NumericToString} See info about setup, above*/ + /** @type {function(number | Decimal): string} See info about setup, above */ this.formatSingleSecondaryEffect = setup.formatSingleSecondaryEffect || this.formatSecondaryEffect; - /** @member{string} color to show numbers in glyph tooltips if boosted */ + /** @type {string} color to show numbers in glyph tooltips if boosted */ this.alteredColor = setup.alteredColor; - /** @member{number} string passed along to tooltip code to ensure proper formatting */ + /** @type {number} string passed along to tooltip code to ensure proper formatting */ this.alterationType = setup.alterationType; - /** @member{Boolean} Indicates whether the effect grows with level or shrinks */ + /** @type {boolean} Indicates whether the effect grows with level or shrinks */ this._biggerIsBetter = undefined; + /** @type {boolean} Determines if effect is disabled while in doomed */ + this._enabledInDoomed = setup.enabledInDoomed ?? false; } /** - * @returns{Boolean} + * @returns {boolean} */ get biggerIsBetter() { if (this._biggerIsBetter === undefined) this._biggerIsBetter = this.checkBiggerIsBetter(); return this._biggerIsBetter; } - /** - * @returns{Number} - */ + get singleDesc() { + const singleDesc = this._singleDesc; + return typeof singleDesc === "function" ? singleDesc() : singleDesc; + } + + get totalDesc() { + const totalDesc = this._totalDesc; + return typeof totalDesc === "function" ? totalDesc() : totalDesc; + } + + get genericDesc() { + const genericDesc = this._genericDesc; + return typeof genericDesc === "function" ? genericDesc() : genericDesc; + } + + get shortDesc() { + const shortDesc = this._shortDesc; + return typeof shortDesc === "function" ? shortDesc() : shortDesc; + } + + get isDisabledByDoomed() { + return Pelle.isDoomed && !this._enabledInDoomed; + } + + /** @returns {number} */ compareValues(effectValueA, effectValueB) { const result = Decimal.compare(effectValueA, effectValueB); return this.biggerIsBetter ? result : -result; } - /** @private */ + /** + * @private + * @returns {boolean} + */ checkBiggerIsBetter() { const baseEffect = new Decimal(this.effect(1, 1.01)); const biggerEffect = new Decimal(this.effect(100, 2)); @@ -134,7 +138,7 @@ class GlyphEffectConfig { static checkInputs(setup) { const KNOWN_KEYS = ["id", "bitmaskIndex", "glyphTypes", "singleDesc", "totalDesc", "genericDesc", "effect", "formatEffect", "formatSingleEffect", "combine", "softcap", "conversion", "formatSecondaryEffect", - "formatSingleSecondaryEffect", "alteredColor", "alterationType", "isGenerated", "shortDesc"]; + "formatSingleSecondaryEffect", "alteredColor", "alterationType", "isGenerated", "shortDesc", "enabledInDoomed"]; const unknownField = Object.keys(setup).find(k => !KNOWN_KEYS.includes(k)); if (unknownField !== undefined) { throw new Error(`Glyph effect "${setup.id}" includes unrecognized field "${unknownField}"`); @@ -157,9 +161,7 @@ class GlyphEffectConfig { } } - /** - * @private - */ + /** @private */ static setupCombine(setup) { let combine = setup.combine; const softcap = setup.softcap; @@ -187,629 +189,24 @@ class GlyphEffectConfig { } } -export const ALTERATION_TYPE = { - ADDITION: 1, - EMPOWER: 2, - BOOST: 3 -}; - export const realityGlyphEffectLevelThresholds = [0, 9000, 15000, 25000]; -GameDatabase.reality.glyphEffects = [ - { - id: "timepow", - bitmaskIndex: 0, - isGenerated: true, - glyphTypes: ["time"], - singleDesc: "Time Dimension power +{value}", - totalDesc: "Time Dimension multipliers ^{value}", - shortDesc: "TD power +{value}", - effect: (level, strength) => 1.01 + Math.pow(level, 0.32) * Math.pow(strength, 0.45) / 75, - formatEffect: x => format(x, 3, 3), - formatSingleEffect: x => format(x - 1, 3, 3), - combine: GlyphCombiner.addExponents, - }, { - id: "timespeed", - bitmaskIndex: 1, - isGenerated: true, - glyphTypes: ["time"], - singleDesc: "Multiply game speed by {value}", - totalDesc: "Game runs ×{value} faster", - genericDesc: "Game speed multiplier", - shortDesc: "Game speed ×{value}", - effect: (level, strength) => (GlyphAlteration.isEmpowered("time") - ? 1 + Math.pow(level, 0.35) - : 1 + Math.pow(level, 0.3) * Math.pow(strength, 0.65) / 20), - formatEffect: x => format(x, 3, 3), - combine: GlyphCombiner.multiply, - alteredColor: () => GlyphAlteration.getEmpowermentColor("time"), - alterationType: ALTERATION_TYPE.EMPOWER - }, { - id: "timeetermult", - bitmaskIndex: 2, - isGenerated: true, - glyphTypes: ["time"], - singleDesc: "Multiply Eternity gain by {value}", - totalDesc: "Eternity gain ×{value}", - genericDesc: "Eternity gain multiplier", - shortDesc: "Eternities ×{value}", - effect: (level, strength) => Math.pow((strength + 3) * level, 0.9) * - Math.pow(3, GlyphAlteration.sacrificeBoost("time")), - formatEffect: x => format(x, 2, 2), - combine: GlyphCombiner.multiply, - alteredColor: () => GlyphAlteration.getBoostColor("time"), - alterationType: ALTERATION_TYPE.BOOST - }, { - id: "timeEP", - bitmaskIndex: 3, - isGenerated: true, - glyphTypes: ["time"], - singleDesc: () => (GlyphAlteration.isAdded("time") - ? "Eternity Point gain ×{value} [and ^]{value2}" - : "Multiply Eternity Point gain by {value}"), - totalDesc: () => (GlyphAlteration.isAdded("time") - ? "Eternity Point gain ×{value} and ^{value2}" - : "Eternity Point gain ×{value}"), - genericDesc: () => (GlyphAlteration.isAdded("time") - ? "Eternity Point gain multiplier and power" - : "Eternity Point gain multiplier"), - shortDesc: () => (GlyphAlteration.isAdded("time") - ? "EP ×{value} and ^{value2}" - : "EP ×{value}"), - effect: (level, strength) => Math.pow(level * strength, 3) * 100, - formatEffect: x => format(x, 2, 3), - combine: GlyphCombiner.multiply, - conversion: x => 1 + Math.log10(x) / 1000, - formatSecondaryEffect: x => format(x, 4, 4), - alteredColor: () => GlyphAlteration.getAdditionColor("time"), - alterationType: ALTERATION_TYPE.ADDITION - }, { - id: "dilationDT", - bitmaskIndex: 4, - isGenerated: true, - glyphTypes: ["dilation"], - singleDesc: "Multiply Dilated Time gain by {value}", - totalDesc: "Dilated Time gain ×{value}", - shortDesc: "DT ×{value}", - effect: (level, strength) => (GlyphAlteration.isEmpowered("dilation") - ? DC.D1_005.pow(level).times(15) - : Decimal.pow(level * strength, 1.5).times(2)), - formatEffect: x => format(x, 2, 1), - combine: GlyphCombiner.multiplyDecimal, - alteredColor: () => GlyphAlteration.getEmpowermentColor("dilation"), - alterationType: ALTERATION_TYPE.EMPOWER - }, { - id: "dilationgalaxyThreshold", - bitmaskIndex: 5, - isGenerated: true, - glyphTypes: ["dilation"], - singleDesc: "Tachyon Galaxy threshold multiplier ×{value}", - genericDesc: "Tachyon Galaxy cost multiplier", - shortDesc: "TG threshold ×{value}", - effect: (level, strength) => 1 - Math.pow(level, 0.17) * Math.pow(strength, 0.35) / 100 - - GlyphAlteration.sacrificeBoost("dilation") / 50, - formatEffect: x => format(x, 3, 3), - alteredColor: () => GlyphAlteration.getBoostColor("dilation"), - alterationType: ALTERATION_TYPE.BOOST, - combine: effects => { - const prod = effects.reduce(Number.prodReducer, 1); - return prod < 0.4 - ? { value: 0.4 - Math.pow(0.4 - prod, 1.7), capped: true } - : { value: prod, capped: false }; - }, - }, { - // TTgen slowly generates TT, value amount is per second, displayed per hour - id: "dilationTTgen", - bitmaskIndex: 6, - isGenerated: true, - glyphTypes: ["dilation"], - singleDesc: () => (GlyphAlteration.isAdded("dilation") - ? "Generates {value} Time Theorems/hour [and\nmultiplies Time Theorem generation by] {value2}" - : "Generates {value} Time Theorems per hour"), - totalDesc: () => (GlyphAlteration.isAdded("dilation") - ? "Generating {value} Time Theorems/hour and Time Theorem generation ×{value2}" - : "Generating {value} Time Theorems per hour"), - genericDesc: () => (GlyphAlteration.isAdded("dilation") - ? "Time Theorem generation and multiplier" - : "Time Theorem generation"), - shortDesc: () => (GlyphAlteration.isAdded("dilation") - ? "{value} TT/hr and TTgen ×{value2}" - : "{value} TT/hr"), - effect: (level, strength) => Math.pow(level * strength, 0.5) / 10000, - /** @type {function(number): string} */ - formatEffect: x => format(3600 * x, 2, 2), - combine: GlyphCombiner.add, - conversion: x => Math.clampMin(Math.pow(10000 * x, 1.6), 1), - formatSecondaryEffect: x => format(x, 2, 2), - alteredColor: () => GlyphAlteration.getAdditionColor("dilation"), - alterationType: ALTERATION_TYPE.ADDITION - }, { - id: "dilationpow", - bitmaskIndex: 7, - isGenerated: true, - glyphTypes: ["dilation"], - singleDesc: "Antimatter Dimension power +{value} while Dilated", - totalDesc: "Antimatter Dimension multipliers ^{value} while Dilated", - genericDesc: "Antimatter Dimensions ^x while Dilated", - shortDesc: "Dilated AD power +{value}", - effect: (level, strength) => 1.1 + Math.pow(level, 0.7) * Math.pow(strength, 0.7) / 25, - formatEffect: x => format(x, 2, 2), - formatSingleEffect: x => format(x - 1, 2, 2), - combine: GlyphCombiner.addExponents, - }, { - id: "replicationspeed", - bitmaskIndex: 8, - isGenerated: true, - glyphTypes: ["replication"], - singleDesc: "Multiply Replication speed by {value}", - totalDesc: "Replication speed ×{value}", - genericDesc: "Replication speed multiplier", - shortDesc: "Replication speed ×{value}", - effect: (level, strength) => (GlyphAlteration.isEmpowered("replication") - ? DC.D1_007.pow(level).times(10) - : Decimal.times(level, strength).times(3)), - formatEffect: x => format(x, 2, 1), - combine: GlyphCombiner.multiplyDecimal, - alteredColor: () => GlyphAlteration.getEmpowermentColor("replication"), - alterationType: ALTERATION_TYPE.EMPOWER - }, { - id: "replicationpow", - bitmaskIndex: 9, - isGenerated: true, - glyphTypes: ["replication"], - singleDesc: "Replicanti multiplier power +{value}", - totalDesc: "Replicanti multiplier ^{value}", - shortDesc: "Replicanti mult. power +{value}", - effect: (level, strength) => 1.1 + Math.pow(level, 0.5) * strength / 25 + - GlyphAlteration.sacrificeBoost("replication") * 3, - formatEffect: x => format(x, 2, 2), - formatSingleEffect: x => format(x - 1, 2, 2), - combine: GlyphCombiner.addExponents, - alteredColor: () => GlyphAlteration.getBoostColor("replication"), - alterationType: ALTERATION_TYPE.BOOST - }, { - id: "replicationdtgain", - bitmaskIndex: 10, - isGenerated: true, - glyphTypes: ["replication"], - singleDesc: () => (GlyphAlteration.isAdded("replication") - ? "Multiply Dilated Time [and Replicanti speed] by \nlog₁₀(replicanti)×{value}" - : "Multiply Dilated Time gain by \nlog₁₀(replicanti)×{value}"), - totalDesc: () => (GlyphAlteration.isAdded("replication") - ? "Dilated Time gain and Replication speed ×(log₁₀(replicanti)×{value})" - : "Dilated Time gain ×(log₁₀(replicanti)×{value})"), - genericDesc: () => (GlyphAlteration.isAdded("replication") - ? "Dilated Time+Replicanti mult (log₁₀(replicanti))" - : "Dilated Time gain multiplier (log₁₀(replicanti))"), - shortDesc: () => (GlyphAlteration.isAdded("replication") - ? "DT and repl. ×log₁₀(repl.)×{value}" - : "DT ×log₁₀(repl.)×{value}"), - effect: (level, strength) => 0.0003 * Math.pow(level, 0.3) * Math.pow(strength, 0.65), - formatEffect: x => format(x, 5, 5), - formatSingleEffect: x => format(x, 5, 5), - // It's bad to stack this one additively (N glyphs acts as a DT mult of N) or multiplicatively (the raw number is - // less than 1), so instead we do a multiplicative stacking relative to the "base" effect of a level 1, 0% glyph. - // We also introduce a 3x mult per glyph after the first, so that stacking level 1, 0% glyphs still has an effect. - // This is still just a flat DT mult when stacking multiple glyphs, but at least it's bigger than 2 or 3. - combine: effects => ({ - value: effects.length === 0 ? 0 : effects.reduce(Number.prodReducer, Math.pow(0.0001, 1 - effects.length)), - capped: false - }), - conversion: x => x, - formatSecondaryEffect: x => format(x, 2, 3), - formatSingleSecondaryEffect: x => format(x, 5, 5), - alteredColor: () => GlyphAlteration.getAdditionColor("replication"), - alterationType: ALTERATION_TYPE.ADDITION - }, { - id: "replicationglyphlevel", - bitmaskIndex: 11, - isGenerated: true, - glyphTypes: ["replication"], - singleDesc: () => `Replicanti scaling for next Glyph level: \n^${format(0.4, 1, 1)} - ➜ ^(${format(0.4, 1, 1)} + {value})`, - totalDesc: () => `Replicanti scaling for next Glyph level: ^${format(0.4, 1, 1)} - ➜ ^(${format(0.4, 1, 1)} + {value})`, - genericDesc: "Replicanti scaling for Glyph level", - shortDesc: "Replicanti pow. for level +{value}", - effect: (level, strength) => Math.pow(Math.pow(level, 0.25) * Math.pow(strength, 0.4), 0.5) / 50, - formatEffect: x => format(x, 3, 3), - combine: effects => { - let sum = effects.reduce(Number.sumReducer, 0); - if (effects.length > 2) sum *= 6 / (effects.length + 4); - return sum > 0.1 - ? { value: 0.1 + 0.2 * (sum - 0.1), capped: true } - : { value: sum, capped: effects.length > 2 }; - } - }, { - id: "infinitypow", - bitmaskIndex: 12, - isGenerated: true, - glyphTypes: ["infinity"], - singleDesc: "Infinity Dimension power +{value}", - totalDesc: "Infinity Dimension multipliers ^{value}", - shortDesc: "ID power +{value}", - effect: (level, strength) => 1.007 + Math.pow(level, 0.21) * Math.pow(strength, 0.4) / 75 + - GlyphAlteration.sacrificeBoost("infinity") / 50, - formatEffect: x => format(x, 3, 3), - formatSingleEffect: x => format(x - 1, 3, 3), - combine: GlyphCombiner.addExponents, - alteredColor: () => GlyphAlteration.getBoostColor("infinity"), - alterationType: ALTERATION_TYPE.BOOST - }, { - id: "infinityrate", - bitmaskIndex: 13, - isGenerated: true, - glyphTypes: ["infinity"], - singleDesc: () => `Infinity Power conversion rate: \n^${formatInt(7)} - ➜ ^(${formatInt(7)} + {value})`, - totalDesc: () => `Infinity Power conversion rate: ^${formatInt(7)} - ➜ ^(${formatInt(7)} + {value})`, - genericDesc: "Infinity Power conversion rate", - shortDesc: "Infinity Power conversion +{value}", - effect: (level, strength) => Math.pow(level, 0.2) * Math.pow(strength, 0.4) * 0.04, - formatEffect: x => format(x, 2, 2), - combine: GlyphCombiner.add, - }, { - id: "infinityIP", - bitmaskIndex: 14, - isGenerated: true, - glyphTypes: ["infinity"], - singleDesc: () => (GlyphAlteration.isAdded("infinity") - ? "Infinity Point gain ×{value} [and ^]{value2}" - : "Multiply Infinity Point gain by {value}"), - totalDesc: () => (GlyphAlteration.isAdded("infinity") - ? "Infinity Point gain ×{value} and ^{value2}" - : "Infinity Point gain ×{value}"), - genericDesc: () => (GlyphAlteration.isAdded("infinity") - ? "Infinity Point gain multiplier and power" - : "Infinity Point gain multiplier"), - shortDesc: () => (GlyphAlteration.isAdded("infinity") - ? "IP ×{value} and ^{value2}" - : "IP ×{value}"), - effect: (level, strength) => Math.pow(level * (strength + 1), 6) * 10000, - formatEffect: x => format(x, 2, 3), - combine: GlyphCombiner.multiply, - // eslint-disable-next-line no-negated-condition - softcap: value => ((Effarig.eternityCap !== undefined) ? Math.min(value, Effarig.eternityCap.toNumber()) : value), - conversion: x => 1 + Math.log10(x) / 1800, - formatSecondaryEffect: x => format(x, 4, 4), - alteredColor: () => GlyphAlteration.getAdditionColor("infinity"), - alterationType: ALTERATION_TYPE.ADDITION - }, { - id: "infinityinfmult", - bitmaskIndex: 15, - isGenerated: true, - glyphTypes: ["infinity"], - singleDesc: "Multiply Infinity gain by {value}", - totalDesc: "Infinity gain ×{value}", - genericDesc: "Infinity gain multiplier", - shortDesc: "Infinities ×{value}", - effect: (level, strength) => (GlyphAlteration.isEmpowered("infinity") - ? DC.D1_02.pow(level) - : Decimal.pow(level * strength, 1.5).times(2)), - formatEffect: x => format(x, 2, 1), - combine: GlyphCombiner.multiplyDecimal, - alteredColor: () => GlyphAlteration.getEmpowermentColor("infinity"), - alterationType: ALTERATION_TYPE.EMPOWER - }, { - id: "powerpow", - bitmaskIndex: 16, - isGenerated: true, - glyphTypes: ["power"], - singleDesc: () => (GlyphAlteration.isAdded("power") - ? "Antimatter Dimension power +{value}\n[and Antimatter Galaxy cost ×]{value2}" - : "Antimatter Dimension power +{value}"), - totalDesc: () => (GlyphAlteration.isAdded("power") - ? "Antimatter Dimension multipliers ^{value} and Antimatter Galaxy cost ×{value2}" - : "Antimatter Dimension multipliers ^{value}"), - genericDesc: () => (GlyphAlteration.isAdded("power") - ? "Antimatter Dimensions multipliers ^x and Antimatter Galaxy cost multiplier" - : "Antimatter Dimension multipliers ^x"), - shortDesc: () => (GlyphAlteration.isAdded("power") - ? "AD power +{value} and AG cost ×{value2}" - : "AD power +{value}"), - effect: (level, strength) => 1.015 + Math.pow(level, 0.2) * Math.pow(strength, 0.4) / 75, - formatEffect: x => format(x, 3, 3), - formatSingleEffect: x => format(x - 1, 3, 3), - combine: GlyphCombiner.addExponents, - conversion: x => 2 / (x + 1), - formatSecondaryEffect: x => format(x, 3, 3), - alteredColor: () => GlyphAlteration.getAdditionColor("power"), - alterationType: ALTERATION_TYPE.ADDITION - }, { - id: "powermult", - bitmaskIndex: 17, - isGenerated: true, - glyphTypes: ["power"], - singleDesc: "Antimatter Dimension multipliers ×{value}", - shortDesc: "AD ×{value}", - effect: (level, strength) => (GlyphAlteration.isEmpowered("power") - ? DC.D11111.pow(level * 220) - : Decimal.pow(level * strength * 10, level * strength * 10)), - formatEffect: x => formatPostBreak(x, 2, 0), - combine: GlyphCombiner.multiplyDecimal, - alteredColor: () => GlyphAlteration.getEmpowermentColor("power"), - alterationType: ALTERATION_TYPE.EMPOWER - }, { - id: "powerdimboost", - bitmaskIndex: 18, - isGenerated: true, - glyphTypes: ["power"], - singleDesc: "Dimension Boost multiplier ×{value}", - genericDesc: "Dimension Boost multiplier", - shortDesc: "Dimboost mult. ×{value}", - effect: (level, strength) => Math.pow(level * strength, 0.5) * - Math.pow(1 + GlyphAlteration.sacrificeBoost("power"), 3), - formatEffect: x => format(x, 2, 2), - combine: GlyphCombiner.multiply, - alteredColor: () => GlyphAlteration.getBoostColor("power"), - alterationType: ALTERATION_TYPE.BOOST - }, { - id: "powerbuy10", - bitmaskIndex: 19, - isGenerated: true, - glyphTypes: ["power"], - singleDesc: () => `Increase the bonus from buying ${formatInt(10)} Antimatter Dimensions by {value}`, - totalDesc: () => `Multiplier from "Buy ${formatInt(10)}" ×{value}`, - genericDesc: () => `"Buy ${formatInt(10)}" bonus increase`, - shortDesc: () => `AD Buy ${formatInt(10)} mult. ×{value}`, - effect: (level, strength) => 1 + level * strength / 12, - formatEffect: x => format(x, 2, 2), - combine: GlyphCombiner.addExponents, - }, { - id: "effarigblackhole", - bitmaskIndex: 20, - isGenerated: true, - glyphTypes: ["effarig"], - singleDesc: "Game speed power +{value}", - totalDesc: "Game speed ^{value}", - genericDesc: "Game speed ^x", - shortDesc: "Game speed power +{value}", - effect: (level, strength) => 1 + Math.pow(level, 0.25) * Math.pow(strength, 0.4) / 75, - formatEffect: x => format(x, 3, 3), - formatSingleEffect: x => format(x - 1, 3, 3), - combine: GlyphCombiner.addExponents, - }, { - id: "effarigrm", - bitmaskIndex: 21, - isGenerated: true, - glyphTypes: ["effarig"], - singleDesc: "Reality Machine multiplier ×{value}", - genericDesc: "Reality Machine multiplier", - shortDesc: "RM ×{value}", - effect: (level, strength) => (GlyphAlteration.isEmpowered("effarig") - ? Math.pow(level, 1.5) - : Math.pow(level, 0.6) * strength), - formatEffect: x => format(x, 2, 2), - combine: GlyphCombiner.multiply, - alteredColor: () => GlyphAlteration.getEmpowermentColor("effarig"), - alterationType: ALTERATION_TYPE.EMPOWER - }, { - id: "effarigglyph", - bitmaskIndex: 22, - isGenerated: true, - glyphTypes: ["effarig"], - singleDesc: "Glyph Instability starting level +{value}", - genericDesc: "Glyph Instability delay", - shortDesc: "Instability delay +{value}", - effect: (level, strength) => Math.floor(10 * Math.pow(level * strength, 0.5)), - formatEffect: x => formatInt(x), - combine: GlyphCombiner.add, - }, { - id: "effarigachievement", - bitmaskIndex: 23, - isGenerated: true, - glyphTypes: ["effarig"], - singleDesc: "Achievement multiplier power +{value}", - totalDesc: "Achievement multiplier ^{value}", - genericDesc: "Achievement multiplier ^x", - shortDesc: "Achievement mult. power +{value}", - effect: (level, strength) => 1 + Math.pow(level, 0.4) * Math.pow(strength, 0.6) / 60 + - GlyphAlteration.sacrificeBoost("effarig") / 10, - formatEffect: x => format(x, 3, 3), - formatSingleEffect: x => format(x - 1, 3, 3), - combine: GlyphCombiner.addExponents, - alteredColor: () => GlyphAlteration.getBoostColor("effarig"), - alterationType: ALTERATION_TYPE.BOOST - }, { - id: "effarigforgotten", - bitmaskIndex: 24, - isGenerated: true, - glyphTypes: ["effarig"], - singleDesc: () => (GlyphAlteration.isAdded("effarig") - ? `Buy ${formatInt(10)} multiplier ^{value} [and\nDimension Boost multiplier ^]{value2}` - : `Bonus from buying ${formatInt(10)} Dimensions ^{value}`), - totalDesc: () => (GlyphAlteration.isAdded("effarig") - ? `Multiplier from "Buy ${formatInt(10)}" ^{value} and Dimension Boost multiplier ^{value2}` - : `Multiplier from "Buy ${formatInt(10)}" ^{value}`), - genericDesc: () => (GlyphAlteration.isAdded("effarig") - ? `"Buy ${formatInt(10)}" and Dimension Boost multipliers ^x` - : `"Buy ${formatInt(10)}" multiplier ^x`), - shortDesc: () => (GlyphAlteration.isAdded("effarig") - ? `Buy ${formatInt(10)} mult. ^{value}, Dimboost mult. ^{value2}` - : `Buy ${formatInt(10)} mult. ^{value}`), - effect: (level, strength) => 1 + 2 * Math.pow(level, 0.25) * Math.pow(strength, 0.4), - formatEffect: x => format(x, 2, 2), - combine: GlyphCombiner.multiply, - conversion: x => Math.pow(x, 0.4), - formatSecondaryEffect: x => format(x, 2, 2), - alteredColor: () => GlyphAlteration.getAdditionColor("effarig"), - alterationType: ALTERATION_TYPE.ADDITION - }, { - id: "effarigdimensions", - bitmaskIndex: 25, - isGenerated: true, - glyphTypes: ["effarig"], - singleDesc: "All dimension power +{value}", - totalDesc: "All dimension multipliers ^{value}", - genericDesc: "All dimension multipliers ^x", - shortDesc: "All Dimension power +{value}", - effect: (level, strength) => 1 + Math.pow(level, 0.25) * Math.pow(strength, 0.4) / 500, - formatEffect: x => format(x, 3, 3), - formatSingleEffect: x => format(x - 1, 3, 3), - combine: GlyphCombiner.addExponents, - }, { - id: "effarigantimatter", - bitmaskIndex: 26, - isGenerated: true, - glyphTypes: ["effarig"], - singleDesc: () => `Antimatter production: ${formatInt(10)}^x ➜ ${formatInt(10)}^(x^{value})`, - genericDesc: "Antimatter production exponent", - shortDesc: "AM production exponent ^{value}", - effect: (level, strength) => 1 + Math.pow(level, 0.25) * Math.pow(strength, 0.4) / 5000, - formatEffect: x => format(x, 4, 4), - combine: GlyphCombiner.multiply, - }, { - id: "timeshardpow", - bitmaskIndex: 27, - isGenerated: true, - // This gets explicitly added to time glyphs elsewhere (once unlocked) - glyphTypes: [], - singleDesc: "Time Shard power +{value}", - totalDesc: "Time Shard gain ^{value}", - genericDesc: "Time Shards ^x", - shortDesc: "Time Shard power +{value}", - effect: (level, strength) => 1 + (strength / 3.5) * Math.pow(level, 0.35) / 400, - formatEffect: x => format(x, 3, 3), - formatSingleEffect: x => format(x - 1, 3, 3), - combine: GlyphCombiner.addExponents, - }, { - id: "cursedgalaxies", - bitmaskIndex: 0, - isGenerated: false, - glyphTypes: ["cursed"], - singleDesc: `All Galaxies are {value} weaker`, - totalDesc: "All Galaxy strength -{value}", - shortDesc: "Galaxy Strength -{value}", - // Multiplies by 0.768 per glyph - effect: (level, strength) => Math.pow((strength / 3.5) * level, -0.03), - formatEffect: x => formatPercents(1 - x, 2), - combine: GlyphCombiner.multiply, - }, { - id: "curseddimensions", - bitmaskIndex: 1, - isGenerated: false, - glyphTypes: ["cursed"], - singleDesc: "All Dimension multipliers ^{value}", - shortDesc: "All Dimensions ^{value}", - // Multiplies by 0.734 per glyph - effect: (level, strength) => Math.pow((strength / 3.5) * level, -0.035), - formatEffect: x => format(x, 3, 3), - combine: GlyphCombiner.multiply, - }, { - id: "cursedtickspeed", - bitmaskIndex: 2, - isGenerated: false, - glyphTypes: ["cursed"], - singleDesc: "The threshold for Tickspeed Upgrades from Time Dimensions is multiplied by ×{value}", - totalDesc: "The threshold for Tickspeed Upgrades from Time Dimensions is increased by ×{value}", - shortDesc: "TD Tickspeed threshold ×{value}", - // Additive 3.82 per glyph - effect: (level, strength) => Math.log10(level) * (strength / 3.5), - formatEffect: x => format(x, 3, 3), - combine: GlyphCombiner.add, - }, { - id: "cursedEP", - bitmaskIndex: 3, - isGenerated: false, - glyphTypes: ["cursed"], - singleDesc: "Divide Eternity Point gain by {value}", - totalDesc: "Eternity Point gain / {value}", - shortDesc: "EP / {value}", - // Divides e666.6 per glyph - effect: (level, strength) => Decimal.pow10(-level / 10 * (strength / 3.5)), - formatEffect: x => format(x.reciprocal()), - combine: GlyphCombiner.multiplyDecimal, - }, { - id: "realityglyphlevel", - bitmaskIndex: 4, - isGenerated: false, - glyphTypes: ["reality"], - singleDesc: "Increase the effective level of equipped basic Glyphs by {value}", - totalDesc: "Equipped basic Glyph level +{value}", - shortDesc: "Basic Glyph Level +{value}", - effect: level => Math.floor(Math.sqrt(level * 90)), - formatEffect: x => formatInt(x), - combine: GlyphCombiner.add, - }, { - id: "realitygalaxies", - bitmaskIndex: 5, - isGenerated: false, - glyphTypes: ["reality"], - singleDesc: "All Galaxies are {value} stronger", - totalDesc: "All Galaxy strength +{value}", - shortDesc: "Galaxy Strength +{value}", - effect: level => 1 + Math.pow(level / 100000, 0.5), - formatEffect: x => formatPercents(x - 1, 2), - combine: GlyphCombiner.multiply, - }, { - id: "realityrow1pow", - bitmaskIndex: 6, - isGenerated: false, - glyphTypes: ["reality"], - singleDesc: "Multiplier from Reality Upgrade Amplifiers ^{value}", - totalDesc: "Reality Upgrade Amplifier multiplier ^{value}", - shortDesc: "Amplifier Multiplier ^{value}", - effect: level => 1 + level / 125000, - formatEffect: x => format(x, 3, 3), - combine: GlyphCombiner.addExponents, - }, { - id: "realityDTglyph", - bitmaskIndex: 7, - isGenerated: false, - glyphTypes: ["reality"], - singleDesc: () => `Dilated Time scaling for next Glyph level: \n^${format(1.3, 1, 1)} - ➜ ^(${format(1.3, 1, 1)} + {value})`, - totalDesc: () => `Dilated Time scaling for next Glyph level: ^${format(1.3, 1, 1)} - ➜ ^(${format(1.3, 1, 1)} + {value})`, - genericDesc: "Dilated Time scaling for Glyph level", - shortDesc: "DT pow. for level +{value}", - // You can only get this effect on level 25000 reality glyphs anyway, might as well make it look nice - effect: () => 0.1, - formatEffect: x => format(x, 2, 2), - combine: GlyphCombiner.add, - }, { - id: "companiondescription", - bitmaskIndex: 8, - isGenerated: false, - glyphTypes: ["companion"], - singleDesc: "It does nothing but sit there and cutely smile at you, whisper into your dreams politely, " + - "and plot the demise of all who stand against you. This one-of-a-kind Glyph will never leave you.", - totalDesc: "+{value} happiness", - shortDesc: "Doesn't want to kill you", - effect: () => (Enslaved.isRunning ? 0 : (0.4 + 0.6 * Math.random())), - formatEffect: x => formatPercents(x, 2, 2), - combine: GlyphCombiner.add, - }, { - id: "companionEP", - bitmaskIndex: 9, - isGenerated: false, - glyphTypes: ["companion"], - singleDesc: "Thanks for your dedication for the game! You reached {value} Eternity Points on your first Reality.", - shortDesc: "It loves you very, very much", - totalDesc: () => (Enslaved.isRunning ? "Help me" : "Yay!"), - // The EP value for this is entirely encoded in rarity, but level needs to be present to - // make sure the proper parameter is being used. The actual glyph level shouldn't do anything. - // eslint-disable-next-line no-unused-vars - effect: (level, strength) => Decimal.pow10(1e6 * strengthToRarity(strength)), - formatEffect: x => formatPostBreak(x, 2), - combine: GlyphCombiner.multiplyDecimal, - } -].mapToObject(effect => effect.id, effect => new GlyphEffectConfig(effect)); +export const GlyphEffects = mapGameDataToObject( + GameDatabase.reality.glyphEffects, + config => new GlyphEffectConfig(config) +); export function findGlyphTypeEffects(glyphType) { - return Object.values(GameDatabase.reality.glyphEffects).filter(e => e.glyphTypes.includes(glyphType)); + return GlyphEffects.all.filter(e => e.glyphTypes.includes(glyphType)); } export function makeGlyphEffectBitmask(effectList) { - // eslint-disable-next-line no-bitwise - return effectList.reduce((mask, eff) => mask + (1 << GameDatabase.reality.glyphEffects[eff].bitmaskIndex), 0); + return effectList.reduce((mask, eff) => mask + (1 << GlyphEffects[eff].bitmaskIndex), 0); } export function getGlyphEffectsFromBitmask(bitmask) { return orderedEffectList - .map(effectName => GameDatabase.reality.glyphEffects[effectName]) - // eslint-disable-next-line no-bitwise + .map(effectName => GlyphEffects[effectName]) .filter(effect => (bitmask & (1 << effect.bitmaskIndex)) !== 0); } @@ -829,142 +226,47 @@ class GlyphType { * @param {string} setup.id * @param {string} setup.symbol * @param {string} setup.color - * @param {function} [setup.primaryEffect] All glyphs generated will have this effect, if specified - * @param {function} [setup.unlockedFn] If this glyph type is not available initially, this specifies + * @param {function(): string} [setup.primaryEffect] All glyphs generated will have this effect, if specified + * @param {function(): boolean} [setup.isUnlocked] If this glyph type is not available initially, this specifies * how to check to see if it is available - * @param {function(string):boolean} [setup.effectUnlockedFn] If certain effects of this glyph are not - * initially available, this is a function of the effect id that returns whether one is * @param {number} setup.alchemyResource Alchemy resource generated by sacrificing this glyph * @param {boolean} setup.hasRarity If the glyph can have rarity or not */ constructor(setup) { - /** @member {string} id identifier for this type (time, power, etc)*/ + /** @type {string} identifier for this type (time, power, etc)*/ this.id = setup.id; - /** @member {string} symbol used to display glyphs of this type and as a UI shorthand */ + /** @type {string} used to display glyphs of this type and as a UI shorthand */ this.symbol = setup.symbol; - /** @member {GlyphEffectConfig[]} effects list of effects that this glyph can have */ + /** @type {GlyphEffectConfig[]} list of effects that this glyph can have */ this.effects = findGlyphTypeEffects(setup.id); - /** @member {string} color used for glyph borders and other places where color coding is needed */ + /** @type {string} used for glyph borders and other places where color coding is needed */ this.color = setup.color; - /** @member {string?} primaryEffect all glyphs generated will have at least this effect */ + /** @type {string?} all glyphs generated will have at least this effect */ this.primaryEffect = setup.primaryEffect; - /** @private @member {function?} unlockedFn */ - this.unlockedFn = setup.unlockedFn; - /** @private @member {function(string):boolean?} effectUnlockedFn */ - this.effectUnlockedFn = setup.effectUnlockedFn; + /** @type {undefined | function(): boolean} */ + this._isUnlocked = setup.isUnlocked; + /** @type {number} */ this.alchemyResource = setup.alchemyResource; + /** @type {boolean} */ this.hasRarity = setup.hasRarity; if (!GLYPH_TYPES.includes(this.id)) { throw new Error(`Id ${this.id} not found in GLYPH_TYPES`); } } - /** @property {boolean} */ + /** @returns {boolean} */ get isUnlocked() { - // eslint-disable-next-line no-negated-condition - return this.unlockedFn !== undefined ? this.unlockedFn() : true; - } - - /** - * @param {string} id - * @returns {boolean} - */ - isEffectUnlocked(id) { - // eslint-disable-next-line no-negated-condition - return this.effectUnlockedFn !== undefined ? this.effectUnlockedFn(id) : true; - } - - /** - * @param {function(): number} rng Random number source (0..1) - * @param {string[]} [blacklist] Do not return the specified effects - * @returns {string | null} - */ - randomEffect(rng, blacklist = []) { - const available = this.effects - .map(e => e.id) - .filter(id => !blacklist.includes(id) && this.isEffectUnlocked(id)); - if (available.length === 0) return null; - return available[Math.floor(rng.uniform() * available.length)]; + return this._isUnlocked?.() ?? true; } } +const allGlyphTypes = mapGameDataToObject( + GameDatabase.reality.glyphTypes, + config => new GlyphType(config) +); + export const GlyphTypes = { - time: new GlyphType({ - id: "time", - symbol: GLYPH_SYMBOLS.time, - effects: findGlyphTypeEffects("time"), - color: "#b241e3", - primaryEffect: "timepow", - alchemyResource: ALCHEMY_RESOURCE.TIME, - hasRarity: true - }), - dilation: new GlyphType({ - id: "dilation", - symbol: GLYPH_SYMBOLS.dilation, - effects: findGlyphTypeEffects("dilation"), - color: "#64dd17", - alchemyResource: ALCHEMY_RESOURCE.DILATION, - hasRarity: true - }), - replication: new GlyphType({ - id: "replication", - symbol: GLYPH_SYMBOLS.replication, - effects: findGlyphTypeEffects("replication"), - color: "#03a9f4", - alchemyResource: ALCHEMY_RESOURCE.REPLICATION, - hasRarity: true - }), - infinity: new GlyphType({ - id: "infinity", - symbol: GLYPH_SYMBOLS.infinity, - effects: findGlyphTypeEffects("infinity"), - color: "#b67f33", - primaryEffect: "infinitypow", - alchemyResource: ALCHEMY_RESOURCE.INFINITY, - hasRarity: true - }), - power: new GlyphType({ - id: "power", - symbol: GLYPH_SYMBOLS.power, - effects: findGlyphTypeEffects("power"), - color: "#22aa48", - primaryEffect: "powerpow", - alchemyResource: ALCHEMY_RESOURCE.POWER, - hasRarity: true - }), - effarig: new GlyphType({ - id: "effarig", - symbol: GLYPH_SYMBOLS.effarig, - effects: findGlyphTypeEffects("effarig"), - color: "#e21717", - unlockedFn: () => EffarigUnlock.reality.isUnlocked, - alchemyResource: ALCHEMY_RESOURCE.EFFARIG, - hasRarity: true - // Effarig glyphs have no primary effect; all are equally likely - }), - reality: new GlyphType({ - id: "reality", - symbol: GLYPH_SYMBOLS.reality, - effects: findGlyphTypeEffects("reality"), - color: "#555555", - unlockedFn: () => false, - alchemyResource: ALCHEMY_RESOURCE.REALITY - // Refining a reality glyph is pretty wasteful anyway, but might as well have this here - }), - cursed: new GlyphType({ - id: "cursed", - symbol: GLYPH_SYMBOLS.cursed, - effects: findGlyphTypeEffects("cursed"), - color: "black", - unlockedFn: () => false, - }), - companion: new GlyphType({ - id: "companion", - symbol: GLYPH_SYMBOLS.companion, - effects: findGlyphTypeEffects("companion"), - color: "#feaec9", - unlockedFn: () => false, - }), + ...allGlyphTypes, /** * @param {function(): number} rng Random number source (0..1) * @param {string} [blacklisted] Do not return the specified type diff --git a/javascripts/core/glyphs/auto-glyph-processor.js b/javascripts/core/glyphs/auto-glyph-processor.js index 9ae59c732..a14e93081 100644 --- a/javascripts/core/glyphs/auto-glyph-processor.js +++ b/javascripts/core/glyphs/auto-glyph-processor.js @@ -23,8 +23,11 @@ export const AutoGlyphProcessor = { if (glyph.type === "cursed") return -Infinity; switch (this.scoreMode) { case AUTO_GLYPH_SCORE.LOWEST_SACRIFICE: - // Picked glyphs are never kept in this mode - return -player.reality.glyphs.sac[glyph.type]; + // Picked glyphs are never kept in this mode. Sacrifice cap needs to be checked since effarig caps + // at a lower value than the others and we don't want to uselessly pick that to sacrifice all the time + return player.reality.glyphs.sac[glyph.type] >= GlyphSacrifice[glyph.type].cap + ? -Infinity + : -player.reality.glyphs.sac[glyph.type]; case AUTO_GLYPH_SCORE.EFFECT_COUNT: // Effect count, plus a very small rarity term to break ties in favor of rarer glyphs return strengthToRarity(glyph.strength) / 1000 + getGlyphEffectsFromBitmask(glyph.effects, 0, 0) @@ -51,7 +54,7 @@ export const AutoGlyphProcessor = { const effectList = getGlyphEffectsFromBitmask(glyph.effects, 0, 0) .filter(effect => effect.isGenerated) .map(effect => effect.id); - // This ternary check is required to filter out the additional effects given by Ra-Enslaved 25, which don't + // This ternary check is required to filter out the additional effects given by Ra-Nameless 25, which don't // exist in the glyph filter settings. It can be safely ignored since the effect is always given. const effectScore = effectList.map(e => (typeCfg.effectScores[e] ? typeCfg.effectScores[e] : 0)).sum(); return strengthToRarity(glyph.strength) + effectScore; @@ -61,7 +64,8 @@ export const AutoGlyphProcessor = { // to make them picked last, because we can't refine them. case AUTO_GLYPH_SCORE.LOWEST_ALCHEMY: { const resource = AlchemyResource[glyph.type]; - return resource.isUnlocked && !resource.capped + const refinementGain = GlyphSacrificeHandler.glyphRefinementGain(glyph); + return resource.isUnlocked && refinementGain > 0 ? -resource.amount : Number.NEGATIVE_INFINITY; } @@ -123,6 +127,12 @@ export const AutoGlyphProcessor = { default: throw new Error("Unknown auto Glyph Sacrifice mode"); } + }, + // Generally only used for UI in order to notify the player that they might end up retroactively getting rid of + // some glyphs they otherwise want to keep + hasNegativeEffectScore() { + return this.scoreMode === AUTO_GLYPH_SCORE.EFFECT_SCORE && + Object.values(this.types).map(t => Object.values(t.effectScores)).flat().some(v => v < 0); } }; @@ -130,10 +140,27 @@ export function autoAdjustGlyphWeights() { const sources = getGlyphLevelSources(); const f = x => Math.pow(Math.clampMin(1, Math.log(5 * x)), 3 / 2); const totalWeight = Object.values(sources).map(s => f(s.value)).sum(); - player.celestials.effarig.glyphWeights.ep = 100 * f(sources.ep.value) / totalWeight; - player.celestials.effarig.glyphWeights.repl = 100 * f(sources.repl.value) / totalWeight; - player.celestials.effarig.glyphWeights.dt = 100 * f(sources.dt.value) / totalWeight; - player.celestials.effarig.glyphWeights.eternities = 100 * f(sources.eternities.value) / totalWeight; + const scaledWeight = key => 100 * f(sources[key].value) / totalWeight; + + // Adjust all weights to be integer, while maintaining that they must sum to 100. We ensure it's within 1 on the + // weights by flooring and then taking guesses on which ones would give the largest boost when adding the lost + // amounts. This isn't necessarily the best integer weighting, but gives a result that's quite literally within + // 99.97% of the non-integer optimal settings and prevents the total from exceeding 100. + const weightKeys = ["ep", "repl", "dt", "eternities"]; + const weights = []; + for (const key of weightKeys) { + weights.push({ + key, + percent: scaledWeight(key) + }); + } + const fracPart = x => x - Math.floor(x); + const priority = weights.sort((a, b) => fracPart(b.percent) - fracPart(a.percent)).map(w => w.key); + const missingPercent = 100 - weights.map(w => Math.floor(w.percent)).reduce((a, b) => a + b); + for (let i = 0; i < weightKeys.length; i++) { + const key = priority[i]; + player.celestials.effarig.glyphWeights[key] = Math.floor(scaledWeight(key)) + (i < missingPercent ? 1 : 0); + } } function getGlyphLevelSources() { @@ -183,6 +210,7 @@ function getGlyphLevelSources() { export function getGlyphLevelInputs() { const sources = getGlyphLevelSources(); + const staticFactors = GameCache.staticGlyphWeights.value; // If the nomial blend of inputs is a * b * c * d, then the contribution can be tuend by // changing the exponents on the terms: aⁿ¹ * bⁿ² * cⁿ³ * dⁿ⁴ // If n1..n4 just add up to 4, then the optimal strategy is to just max out the one over the @@ -221,55 +249,65 @@ export function getGlyphLevelInputs() { adjustFactor(sources.repl, weights.repl / 100); adjustFactor(sources.dt, weights.dt / 100); adjustFactor(sources.eternities, weights.eternities / 100); - const perkShopEffect = Effects.max(1, PerkShopUpgrade.glyphLevel); - const shardFactor = Ra.has(RA_UNLOCKS.SHARD_LEVEL_BOOST) ? RA_UNLOCKS.SHARD_LEVEL_BOOST.effect() : 0; + const shardFactor = Ra.unlocks.relicShardGlyphLevelBoost.effectOrDefault(0); let baseLevel = sources.ep.value * sources.repl.value * sources.dt.value * sources.eternities.value * - perkShopEffect + shardFactor; + staticFactors.perkShop + shardFactor; - const singularityEffect = SingularityMilestone.glyphLevelFromSingularities.isUnlocked - ? SingularityMilestone.glyphLevelFromSingularities.effectValue - : 1; + const singularityEffect = SingularityMilestone.glyphLevelFromSingularities.effectOrDefault(1); baseLevel *= singularityEffect; let scaledLevel = baseLevel; - // With begin = 1000 and rate = 250, a base level of 2000 turns into 1500; 4000 into 2000 - const instabilityScaleBegin = Glyphs.instabilityThreshold; - const instabilityScaleRate = 500; - if (scaledLevel > instabilityScaleBegin) { - const excess = (scaledLevel - instabilityScaleBegin) / instabilityScaleRate; - scaledLevel = instabilityScaleBegin + 0.5 * instabilityScaleRate * (Math.sqrt(1 + 4 * excess) - 1); - } - const hyperInstabilityScaleBegin = Glyphs.hyperInstabilityThreshold; - const hyperInstabilityScaleRate = 400; - if (scaledLevel > hyperInstabilityScaleBegin) { - const excess = (scaledLevel - hyperInstabilityScaleBegin) / hyperInstabilityScaleRate; - scaledLevel = hyperInstabilityScaleBegin + 0.5 * hyperInstabilityScaleRate * (Math.sqrt(1 + 4 * excess) - 1); - } + // The softcap starts at begin and rate determines how quickly level scales after the cap, turning a linear pre-cap + // increase to a quadratic post-cap increase with twice the scaling. For example, with begin = 1000 and rate = 400: + // - Scaled level 1400 requires +800 more base levels from the start of the cap (ie. level 1800) + // - Scaled level 1800 requires +1600 more base levels from scaled 1400 (ie. level 3400) + // - Each additional 400 scaled requires another +800 on top of the already-existing gap for base + // This is applied twice in a stacking way, using regular instability first and then again with hyperinstability + // if the newly reduced level is still above the second threshold + const instabilitySoftcap = (level, begin, rate) => { + if (level < begin) return level; + const excess = (level - begin) / rate; + return begin + 0.5 * rate * (Math.sqrt(1 + 4 * excess) - 1); + }; + scaledLevel = instabilitySoftcap(scaledLevel, staticFactors.instability, 500); + scaledLevel = instabilitySoftcap(scaledLevel, staticFactors.hyperInstability, 400); + const scalePenalty = scaledLevel > 0 ? baseLevel / scaledLevel : 1; - const rowFactor = [Array.range(1, 5).every(x => RealityUpgrade(x).boughtAmount > 0)] - .concat(Array.range(1, 4).map(x => Array.range(1, 5).every(y => RealityUpgrade(5 * x + y).isBought))) - .filter(x => x) - .length; - const achievementFactor = Effects.sum(Achievement(148), Achievement(166)); - baseLevel += rowFactor + achievementFactor; - scaledLevel += rowFactor + achievementFactor; - // Temporary runaway prevention (?) - const levelHardcap = 1000000; - const levelCapped = scaledLevel > levelHardcap; - scaledLevel = Math.min(scaledLevel, levelHardcap); + const incAfterInstability = staticFactors.realityUpgrades + staticFactors.achievements; + baseLevel += incAfterInstability; + scaledLevel += incAfterInstability; return { ep: sources.ep, repl: sources.repl, dt: sources.dt, eter: sources.eternities, - perkShop: perkShopEffect, + perkShop: staticFactors.perkShop, scalePenalty, - rowFactor, - achievementFactor, + rowFactor: staticFactors.realityUpgrades, + achievementFactor: staticFactors.achievements, shardFactor, singularityEffect, rawLevel: baseLevel, actualLevel: Math.max(1, scaledLevel), - capped: levelCapped + }; +} + +// Calculates glyph weights which don't change over the course of a reality unless particular events occur; this is +// stored in the GameCache and only invalidated as needed +export function staticGlyphWeights() { + const perkShop = Effects.max(1, PerkShopUpgrade.glyphLevel); + const instability = Glyphs.instabilityThreshold; + const hyperInstability = Glyphs.hyperInstabilityThreshold; + const realityUpgrades = [Array.range(1, 5).every(x => RealityUpgrade(x).boughtAmount > 0)] + .concat(Array.range(1, 4).map(x => Array.range(1, 5).every(y => RealityUpgrade(5 * x + y).isBought))) + .filter(x => x) + .length; + const achievements = Effects.sum(Achievement(148), Achievement(166)); + return { + perkShop, + instability, + hyperInstability, + realityUpgrades, + achievements }; } diff --git a/javascripts/core/glyphs/glyph-core.js b/javascripts/core/glyphs/glyph-core.js index 7791049c1..e01db6f6d 100644 --- a/javascripts/core/glyphs/glyph-core.js +++ b/javascripts/core/glyphs/glyph-core.js @@ -1,10 +1,11 @@ -import { GameMechanicState } from "../game-mechanics/index.js"; +import { GameMechanicState } from "../game-mechanics/index"; export const orderedEffectList = ["powerpow", "infinitypow", "replicationpow", "timepow", "dilationpow", "timeshardpow", "powermult", "powerdimboost", "powerbuy10", "dilationTTgen", "infinityinfmult", "infinityIP", "timeEP", - "dilationDT", "replicationdtgain", "replicationspeed", "timespeed", + "dilationDT", "replicationdtgain", "replicationspeed", "timeetermult", "dilationgalaxyThreshold", "infinityrate", "replicationglyphlevel", + "timespeed", "effarigblackhole", "effarigrm", "effarigglyph", "effarigachievement", "effarigforgotten", "effarigdimensions", "effarigantimatter", "cursedgalaxies", "cursedtickspeed", "curseddimensions", "cursedEP", @@ -51,13 +52,15 @@ export const Glyphs = { const isUsableIndex = index => (useProtectedSlots ? index < this.protectedSlots : index >= this.protectedSlots); return this.inventory.findIndex((slot, index) => slot === null && isUsableIndex(index)); }, + // This is stored in GameCache and only invalidated if glyphs change; we check for free inventory space often in + // lots of places and this is an expensive operation get freeInventorySpace() { this.validate(); return this.inventory.filter((e, idx) => e === null && idx >= this.protectedSlots).length; }, get activeSlotCount() { if (Pelle.isDoomed) { - if (PelleRifts.famine.milestones[0].canBeApplied) return 1; + if (PelleRifts.vacuum.milestones[0].canBeApplied) return 1; return 0; } return 3 + Effects.sum(RealityUpgrade(9), RealityUpgrade(24)); @@ -115,6 +118,7 @@ export const Glyphs = { player.reality.glyphs.protectedRows = newRows; this.validate(); + GameCache.glyphInventorySpace.invalidate(); }, // Move all glyphs from the origin row to the destination row, does nothing if a column-preserving move operation // isn't possible. Returns a boolean indicating success/failure on glyph moving. Row is 0-indexed @@ -183,15 +187,38 @@ export const Glyphs = { this.validate(); EventHub.dispatch(GAME_EVENT.GLYPHS_CHANGED); }, - findByValues(finding, ignore = { level, strength, effects }) { - for (const glyph of this.sortedInventoryList) { - const type = glyph.type === finding.type; - const effects = glyph.effects === finding.effects || - (ignore.effects && hasAtLeastGlyphEffects(glyph.effects, finding.effects)); - const str = ignore.strength || glyph.strength === finding.strength; - const lvl = ignore.level || glyph.level === finding.level; - const sym = Boolean(glyph.symbol) || glyph.symbol === finding.symbol; - if (type && effects && str && lvl && sym) return glyph; + findByValues(targetGlyph, searchList, fuzzyMatch = { level, strength, effects }) { + // We need comparison to go both ways for normal matching and subset matching for partially-equipped sets + const compFn = (op, comp1, comp2) => { + switch (op) { + case -1: + return comp1 <= comp2; + case 0: + return comp1 === comp2; + case 1: + return comp1 >= comp2; + } + return false; + }; + + for (const glyph of searchList) { + const type = glyph.type === targetGlyph.type; + let eff = false; + switch (fuzzyMatch.effects) { + case -1: + eff = hasAtLeastGlyphEffects(targetGlyph.effects, glyph.effects); + break; + case 0: + eff = glyph.effects === targetGlyph.effects; + break; + case 1: + eff = hasAtLeastGlyphEffects(glyph.effects, targetGlyph.effects); + break; + } + const str = compFn(fuzzyMatch.strength, glyph.strength, targetGlyph.strength); + const lvl = compFn(fuzzyMatch.level, glyph.level, targetGlyph.level); + const sym = glyph.symbol === targetGlyph.symbol; + if (type && eff && str && lvl && sym) return glyph; } return undefined; }, @@ -216,6 +243,8 @@ export const Glyphs = { ) ) return; + if (GameEnd.creditsEverClosed) return; + this.validate(); if (this.findByInventoryIndex(glyph.idx) !== glyph) { throw new Error("Inconsistent inventory indexing"); @@ -226,7 +255,8 @@ export const Glyphs = { } if (this.active[targetSlot] === null) { if (sameSpecialTypeIndex >= 0) { - Modal.message.show(`You may only have one ${glyph.type.capitalize()} Glyph equipped`); + Modal.message.show(`You may only have one ${glyph.type.capitalize()} Glyph equipped!`, + { closeEvent: GAME_EVENT.GLYPHS_CHANGED }); return; } this.removeFromInventory(glyph); @@ -242,7 +272,8 @@ export const Glyphs = { } else { // We can only replace effarig/reality glyph if (sameSpecialTypeIndex >= 0 && sameSpecialTypeIndex !== targetSlot) { - Modal.message.show(`You may only have one ${glyph.type.capitalize()} Glyph equipped`); + Modal.message.show(`You may only have one ${glyph.type.capitalize()} Glyph equipped!`, + { closeEvent: GAME_EVENT.GLYPHS_CHANGED }); return; } if (!player.options.confirmations.glyphReplace) { @@ -254,9 +285,10 @@ export const Glyphs = { // Loading glyph sets might choose NEW! glyphs, in which case the hover-over flag clearing never got triggered this.removeNewFlag(glyph); }, - unequipAll() { + unequipAll(forceToUnprotected = false) { + const targetRegion = forceToUnprotected ? false : player.options.respecIntoProtected; while (player.reality.glyphs.active.length) { - const freeIndex = this.findFreeIndex(player.options.respecIntoProtected); + const freeIndex = this.findFreeIndex(targetRegion); if (freeIndex < 0) break; const glyph = player.reality.glyphs.active.pop(); this.active[glyph.idx] = null; @@ -323,7 +355,7 @@ export const Glyphs = { }, addToInventory(glyph, requestedInventoryIndex, isExistingGlyph = false) { this.validate(); - glyph.id = GlyphGenerator.makeID(); + if (!isExistingGlyph) glyph.id = GlyphGenerator.makeID(); const isProtectedIndex = requestedInventoryIndex < this.protectedSlots; let index = this.findFreeIndex(isProtectedIndex); if (index < 0) return; @@ -380,7 +412,7 @@ export const Glyphs = { }, sort(sortFunction) { const glyphsToSort = player.reality.glyphs.inventory.filter(g => g.idx >= this.protectedSlots); - const freeSpace = this.freeInventorySpace; + const freeSpace = GameCache.glyphInventorySpace.value; const sortOrder = ["power", "infinity", "replication", "time", "dilation", "effarig", "reality", "cursed", "companion"]; const byType = sortOrder.mapToObject(g => g, () => ({ glyphs: [], padding: 0 })); @@ -421,7 +453,6 @@ export const Glyphs = { }, sortByEffect() { function reverseBitstring(eff) { - // eslint-disable-next-line no-bitwise return parseInt(((1 << 30) + (eff >>> 0)).toString(2).split("").reverse().join(""), 2); } // The bitwise reversal is so that the effects with the LOWER id are valued higher in the sorting. @@ -431,6 +462,7 @@ export const Glyphs = { // If there are enough glyphs that are better than the specified glyph, in every way, then // the glyph is objectively a useless piece of garbage. isObjectivelyUseless(glyph, threshold) { + if (player.reality.applyFilterToPurge && AutoGlyphProcessor.wouldKeep(glyph)) return false; function hasSomeBetterEffects(glyphA, glyphB, comparedEffects) { for (const effect of comparedEffects) { const c = effect.compareValues( @@ -446,7 +478,6 @@ export const Glyphs = { g.type === glyph.type && g.id !== glyph.id && (g.level >= glyph.level || g.strength >= glyph.strength) && - // eslint-disable-next-line no-bitwise ((g.effects & glyph.effects) === glyph.effects)); let compareThreshold = glyph.type === "effarig" || glyph.type === "reality" ? 1 : 5; compareThreshold = Math.clampMax(compareThreshold, threshold); @@ -455,6 +486,7 @@ export const Glyphs = { const betterCount = toCompare.countWhere(other => !hasSomeBetterEffects(glyph, other, comparedEffects)); return betterCount >= compareThreshold; }, + // Note that this same function is called with different parameters for purge (5), harsh purge (1), and sac all (0) autoClean(threshold = 5, deleteGlyphs = true) { const isHarsh = threshold < 5; let toBeDeleted = 0; @@ -463,7 +495,8 @@ export const Glyphs = { // We look in backwards order so that later glyphs get cleaned up first for (let inventoryIndex = this.totalSlots - 1; inventoryIndex >= this.protectedSlots; --inventoryIndex) { const glyph = this.inventory[inventoryIndex]; - if (glyph === null || glyph.type === "companion") continue; + // Never clean companion, and only clean cursed if we choose to sacrifice all + if (glyph === null || glyph.type === "companion" || (glyph.type === "cursed" && threshold !== 0)) continue; // Don't auto-clean custom glyphs (eg. music glyphs) unless it's harsh or delete all const isCustomGlyph = glyph.color !== undefined || glyph.symbol !== undefined; if (isCustomGlyph && !isHarsh) continue; @@ -503,7 +536,7 @@ export const Glyphs = { } }, processSortingAfterReality() { - if (V.has(V_UNLOCKS.AUTO_AUTOCLEAN) && player.reality.autoAutoClean) this.autoClean(); + if (VUnlocks.autoAutoClean.canBeApplied && player.reality.autoAutoClean) this.autoClean(); switch (player.reality.autoSort) { case AUTO_SORT_MODE.NONE: break; @@ -609,13 +642,11 @@ export const Glyphs = { }, // Modifies a basic glyph to have timespeed, and adds the new effect to time glyphs applyGamespeed(glyph) { - if (!Ra.has(RA_UNLOCKS.ALWAYS_GAMESPEED)) return; + if (!Ra.unlocks.allGamespeedGlyphs.canBeApplied) return; if (BASIC_GLYPH_TYPES.includes(glyph.type)) { - // eslint-disable-next-line no-bitwise - glyph.effects |= (1 << GameDatabase.reality.glyphEffects.timespeed.bitmaskIndex); + glyph.effects |= (1 << GlyphEffects.timespeed.bitmaskIndex); if (glyph.type === "time") { - // eslint-disable-next-line no-bitwise - glyph.effects |= (1 << GameDatabase.reality.glyphEffects.timeshardpow.bitmaskIndex); + glyph.effects |= (1 << GlyphEffects.timeshardpow.bitmaskIndex); } } }, @@ -635,23 +666,28 @@ export const Glyphs = { EventHub.dispatch(GAME_EVENT.GLYPHS_EQUIPPED_CHANGED); EventHub.dispatch(GAME_EVENT.GLYPHS_CHANGED); this.validate(); + }, + // Mostly used for key-swapping glyph set UI elements; composites the entire glyph set together in a way which is + // relatively unlikely to cause collisions between different glyph sets unless they're actually the same glyphs. + // Different permutations of the same glyphs should produce the same hash, but aren't guaranteed to + hash(glyphSet) { + let hash = 1; + for (const glyph of glyphSet) { + // This should be at most around e23 or so in practice + const singleGlyphHash = Math.pow(glyph.level, 2) * Math.pow(glyph.strength, 4) * glyph.effects * + glyph.type.charCodeAt(0); + hash *= singleGlyphHash; + } + return hash; } }; class GlyphSacrificeState extends GameMechanicState { } -export const GlyphSacrifice = (function() { - const db = GameDatabase.reality.glyphSacrifice; - return { - time: new GlyphSacrificeState(db.time), - dilation: new GlyphSacrificeState(db.dilation), - replication: new GlyphSacrificeState(db.replication), - infinity: new GlyphSacrificeState(db.infinity), - power: new GlyphSacrificeState(db.power), - effarig: new GlyphSacrificeState(db.effarig), - reality: new GlyphSacrificeState(db.reality), - }; -}()); +export const GlyphSacrifice = mapGameDataToObject( + GameDatabase.reality.glyphSacrifice, + config => new GlyphSacrificeState(config) +); export function recalculateAllGlyphs() { for (let i = 0; i < player.reality.glyphs.active.length; i++) { @@ -687,18 +723,25 @@ export function getRarity(x) { return GlyphRarities.find(e => x >= e.minStrength); } -export function getAdjustedGlyphLevel(glyph, realityGlyphBoost = Glyphs.levelBoost) { +export function getColor(strength) { + return getRarity(strength)[(player.options.forceDarkGlyphs || Theme.current().isDark()) ? "darkColor" : "lightColor"]; +} + +export function getAdjustedGlyphLevel(glyph, realityGlyphBoost = Glyphs.levelBoost, ignoreCelestialEffects = false) { const level = glyph.level; - if (Pelle.isDoomed) return Math.min(level, Pelle.glyphMaxLevel); - if (Enslaved.isRunning) return Math.max(level, Enslaved.glyphLevelMin); - if (Effarig.isRunning) return Math.min(level, Effarig.glyphLevelCap); + if (!ignoreCelestialEffects) { + if (Pelle.isDoomed) return Math.min(level, Pelle.glyphMaxLevel); + if (Enslaved.isRunning) return Math.max(level, Enslaved.glyphLevelMin); + if (Effarig.isRunning) return Math.min(level, Effarig.glyphLevelCap); + } if (BASIC_GLYPH_TYPES.includes(glyph.type)) return level + realityGlyphBoost; return level; } export function respecGlyphs() { if (!Glyphs.unequipAll()) { - Modal.message.show("Some of your Glyphs could not be unequipped due to lack of inventory space."); + Modal.message.show("Some of your Glyphs could not be unequipped due to lack of inventory space.", + { closeEvent: GAME_EVENT.GLYPHS_CHANGED }); } player.reality.respec = false; } diff --git a/javascripts/core/glyphs/glyph-effects.js b/javascripts/core/glyphs/glyph-effects.js index 131313375..90a3d8607 100644 --- a/javascripts/core/glyphs/glyph-effects.js +++ b/javascripts/core/glyphs/glyph-effects.js @@ -53,7 +53,7 @@ export function getAdjustedGlyphEffect(effectKey) { * @return {number | Decimal} */ export function getSecondaryGlyphEffect(effectKey) { - return GameDatabase.reality.glyphEffects[effectKey].conversion(getAdjustedGlyphEffect(effectKey)); + return GlyphEffects[effectKey].conversion(getAdjustedGlyphEffect(effectKey)); } /** @@ -66,15 +66,14 @@ export function getGlyphEffectValues(effectKey) { throw new Error(`Unknown Glyph effect requested "${effectKey}"'`); } return player.reality.glyphs.active - // eslint-disable-next-line no-bitwise - .filter(glyph => ((1 << GameDatabase.reality.glyphEffects[effectKey].bitmaskIndex) & glyph.effects) !== 0) - .filter(glyph => generatedTypes.includes(glyph.type) === GameDatabase.reality.glyphEffects[effectKey].isGenerated) + .filter(glyph => ((1 << GlyphEffects[effectKey].bitmaskIndex) & glyph.effects) !== 0) + .filter(glyph => generatedTypes.includes(glyph.type) === GlyphEffects[effectKey].isGenerated) .map(glyph => getSingleGlyphEffectFromBitmask(effectKey, glyph)); } // Combines all specified glyph effects, reduces some boilerplate function getTotalEffect(effectKey) { - return GameDatabase.reality.glyphEffects[effectKey].combine(getGlyphEffectValues(effectKey)); + return GlyphEffects[effectKey].combine(getGlyphEffectValues(effectKey)); } /** @@ -109,8 +108,7 @@ export function getGlyphEffectValuesFromBitmask(bitmask, level, baseStrength, ty // Pulls out a single effect value from a glyph's bitmask, returning just the value (nothing for missing effects) export function getSingleGlyphEffectFromBitmask(effectName, glyph) { - const glyphEffect = GameDatabase.reality.glyphEffects[effectName]; - // eslint-disable-next-line no-bitwise + const glyphEffect = GlyphEffects[effectName]; if ((glyph.effects & (1 << glyphEffect.bitmaskIndex)) === 0) { return undefined; } @@ -122,9 +120,7 @@ export function countValuesFromBitmask(bitmask) { let numEffects = 0; let bits = bitmask; while (bits !== 0) { - // eslint-disable-next-line no-bitwise numEffects += bits & 1; - // eslint-disable-next-line no-bitwise bits >>= 1; } return numEffects; @@ -137,7 +133,7 @@ export function getActiveGlyphEffects() { .filter(ev => ev.values.length > 0) .map(ev => ({ id: ev.effect, - value: GameDatabase.reality.glyphEffects[ev.effect].combine(ev.values), + value: GlyphEffects[ev.effect].combine(ev.values), })); const effectNames = effectValues.map(e => e.id); diff --git a/javascripts/core/glyphs/glyph-generator.js b/javascripts/core/glyphs/glyph-generator.js index c62299adf..ffa8a3e29 100644 --- a/javascripts/core/glyphs/glyph-generator.js +++ b/javascripts/core/glyphs/glyph-generator.js @@ -151,10 +151,9 @@ export const GlyphGenerator = { // These Glyphs are given on entering Doomed to prevent the player // from having none of each basic glyphs which are requied to beat pelle doomedGlyph(type) { - const effectList = Object.values(GameDatabase.reality.glyphEffects).filter(e => e.id.startsWith(type)); - effectList.push(GameDatabase.reality.glyphEffects.timespeed); + const effectList = GlyphEffects.all.filter(e => e.id.startsWith(type)); + effectList.push(GlyphEffects.timespeed); let bitmask = 0; - // eslint-disable-next-line no-bitwise for (const effect of effectList) bitmask |= 1 << effect.bitmaskIndex; const glyphLevel = Math.max(player.records.bestReality.glyphLevel, 5000); return { @@ -213,9 +212,9 @@ export const GlyphGenerator = { randomStrength(rng) { // Technically getting this upgrade really changes glyph gen but at this point almost all // the RNG is gone anyway. - if (Ra.has(RA_UNLOCKS.MAX_RARITY_AND_SHARD_SACRIFICE_BOOST)) return rarityToStrength(100); + if (Ra.unlocks.maxGlyphRarityAndShardSacrificeBoost.canBeApplied) return rarityToStrength(100); let result = GlyphGenerator.gaussianBellCurve(rng) * GlyphGenerator.strengthMultiplier; - const relicShardFactor = Ra.has(RA_UNLOCKS.EXTRA_CHOICES_AND_RELIC_SHARD_RARITY_ALWAYS_MAX) ? 1 : rng.uniform(); + const relicShardFactor = Ra.unlocks.extraGlyphChoicesAndRelicShardRarityAlwaysMax.canBeApplied ? 1 : rng.uniform(); const increasedRarity = relicShardFactor * Effarig.maxRarityBoost + Effects.sum(Achievement(146), GlyphSacrifice.effarig); // Each rarity% is 0.025 strength. @@ -232,25 +231,25 @@ export const GlyphGenerator = { // as preventing all of the glyphs changing drastically when RU17 is purchased. const random1 = rng.uniform(); const random2 = rng.uniform(); - if (type !== "effarig" && Ra.has(RA_UNLOCKS.GLYPH_EFFECT_COUNT)) return 4; - const maxEffects = Ra.has(RA_UNLOCKS.GLYPH_EFFECT_COUNT) ? 7 : 4; + if (type !== "effarig" && Ra.unlocks.glyphEffectCount.canBeApplied) return 4; + const maxEffects = Ra.unlocks.glyphEffectCount.canBeApplied ? 7 : 4; let num = Math.min( maxEffects, - Math.floor(Math.pow(random1, 1 - (Math.pow(level * strength, 0.5)) / 100) * 1.5 + 1)); + Math.floor(Math.pow(random1, 1 - (Math.pow(level * strength, 0.5)) / 100) * 1.5 + 1) + ); // If we do decide to add anything else that boosts chance of an extra effect, keeping the code like this // makes it easier to do (add it to the Effects.max). if (RealityUpgrade(17).isBought && random2 < Effects.max(0, RealityUpgrade(17))) { num = Math.min(num + 1, maxEffects); } - if (Ra.has(RA_UNLOCKS.GLYPH_EFFECT_COUNT)) num = Math.max(num, 4); - return num; + return Ra.unlocks.glyphEffectCount.canBeApplied ? Math.max(num, 4) : num; }, // Populate a list of reality glyph effects based on level generateRealityEffects(level) { const numberOfEffects = realityGlyphEffectLevelThresholds.filter(lv => lv <= level).length; - const sortedRealityEffects = Object.values(GameDatabase.reality.glyphEffects) - .filter(eff => eff.id.match("reality*")) + const sortedRealityEffects = GlyphEffects.all + .filter(eff => eff.glyphTypes.includes("reality")) .sort((a, b) => a.bitmaskIndex - b.bitmaskIndex) .map(eff => eff.id); return sortedRealityEffects.slice(0, numberOfEffects); diff --git a/javascripts/core/glyphs/glyph-purge-handler.js b/javascripts/core/glyphs/glyph-purge-handler.js index 9221b451d..e454fd836 100644 --- a/javascripts/core/glyphs/glyph-purge-handler.js +++ b/javascripts/core/glyphs/glyph-purge-handler.js @@ -2,11 +2,21 @@ export const GlyphSacrificeHandler = { // Anything scaling on sacrifice caps at this value, even though the actual sacrifice values can go higher maxSacrificeForEffects: 1e100, + // This is used for glyph UI-related things in a few places, but is handled here as a getter which is only called + // sparingly - that is, whenever the cache is invalidated after a glyph is sacrificed. Thus it only gets recalculated + // when glyphs are actually sacrificed, rather than every render cycle. + get logTotalSacrifice() { + // We check elsewhere for this equalling zero to determine if the player has ever sacrificed. Technically this + // should check for -Infinity, but the clampMin works in practice because the minimum possible sacrifice + // value is greater than 1 for even the weakest possible glyph + return BASIC_GLYPH_TYPES.reduce( + (tot, type) => tot + Math.log10(Math.clampMin(player.reality.glyphs.sac[type], 1)), 0); + }, get canSacrifice() { return RealityUpgrade(19).isBought; }, get isRefining() { - return Ra.has(RA_UNLOCKS.GLYPH_ALCHEMY) && AutoGlyphProcessor.sacMode !== AUTO_GLYPH_REJECT.SACRIFICE; + return Ra.unlocks.unlockGlyphAlchemy.canBeApplied && AutoGlyphProcessor.sacMode !== AUTO_GLYPH_REJECT.SACRIFICE; }, handleSpecialGlyphTypes(glyph) { switch (glyph.type) { @@ -35,7 +45,7 @@ export const GlyphSacrificeHandler = { if (glyph.type === "reality") return 0.01 * glyph.level * Achievement(171).effectOrDefault(1); const pre10kFactor = Math.pow(Math.clampMax(glyph.level, 10000) + 10, 2.5); const post10kFactor = 1 + Math.clampMin(glyph.level - 10000, 0) / 100; - const power = Ra.has(RA_UNLOCKS.MAX_RARITY_AND_SHARD_SACRIFICE_BOOST) ? 1 + Effarig.maxRarityBoost / 100 : 1; + const power = Ra.unlocks.maxGlyphRarityAndShardSacrificeBoost.effectOrDefault(1); return Math.pow(pre10kFactor * post10kFactor * glyph.strength * Teresa.runRewardMultiplier * Achievement(171).effectOrDefault(1), power); }, @@ -50,6 +60,7 @@ export const GlyphSacrificeHandler = { return; } player.reality.glyphs.sac[glyph.type] += toGain; + GameCache.logTotalGlyphSacrifice.invalidate(); Glyphs.removeFromInventory(glyph); EventHub.dispatch(GAME_EVENT.GLYPH_SACRIFICED, glyph); }, @@ -64,13 +75,13 @@ export const GlyphSacrificeHandler = { // Refined glyphs give this proportion of their maximum attainable value from their level glyphRefinementEfficiency: 0.05, glyphRawRefinementGain(glyph) { - if (!Ra.has(RA_UNLOCKS.GLYPH_ALCHEMY)) return 0; + if (!Ra.unlocks.unlockGlyphAlchemy.canBeApplied) return 0; const glyphMaxValue = this.levelRefinementValue(glyph.level); const rarityModifier = strengthToRarity(glyph.strength) / 100; return this.glyphRefinementEfficiency * glyphMaxValue * rarityModifier; }, glyphRefinementGain(glyph) { - if (!Ra.has(RA_UNLOCKS.GLYPH_ALCHEMY) || !generatedTypes.includes(glyph.type)) return 0; + if (!Ra.unlocks.unlockGlyphAlchemy.canBeApplied || !generatedTypes.includes(glyph.type)) return 0; const resource = this.glyphAlchemyResource(glyph); const glyphActualValue = this.glyphRawRefinementGain(glyph); if (resource.cap === 0) return glyphActualValue; @@ -96,7 +107,7 @@ export const GlyphSacrificeHandler = { return; } const decoherence = AlchemyResource.decoherence.isUnlocked; - if (!Ra.has(RA_UNLOCKS.GLYPH_ALCHEMY) || + if (!Ra.unlocks.unlockGlyphAlchemy.canBeApplied || (this.glyphRefinementGain(glyph) === 0 && !decoherence) || (decoherence && AlchemyResources.base.every(x => x.data.amount >= Ra.alchemyResourceCap))) { this.sacrificeGlyph(glyph, force); diff --git a/javascripts/core/hotkeys.js b/javascripts/core/hotkeys.js index f52800e54..170d2312c 100644 --- a/javascripts/core/hotkeys.js +++ b/javascripts/core/hotkeys.js @@ -1,6 +1,7 @@ -import { GameKeyboard } from "./keyboard.js"; import Mousetrap from "mousetrap"; +import { GameKeyboard } from "./keyboard"; + // Add your hotkeys and combinations here // GameKeyboard.bind for single press combinations // GameKeyboard.bindRepeatable for repeatable combinations @@ -52,37 +53,40 @@ export const shortcuts = [ name: "Dimension Boost", keys: ["d"], type: "bindRepeatableHotkey", - function: () => requestDimensionBoost(true), + function: () => manualRequestDimensionBoost(true), visible: true }, { name: "Single Dimension Boost", keys: ["shift", "d"], type: "bindRepeatableHotkey", - function: () => requestDimensionBoost(false), + function: () => manualRequestDimensionBoost(false), visible: false }, { name: "Antimatter Galaxy", keys: ["g"], type: "bindRepeatableHotkey", - function: () => requestGalaxyReset(true), + function: () => manualRequestGalaxyReset(true), visible: true }, { name: "Single Antimatter Galaxy", keys: ["shift", "g"], type: "bindRepeatableHotkey", - function: () => requestGalaxyReset(false), + function: () => manualRequestGalaxyReset(false), visible: false }, { name: "Big Crunch", keys: ["c"], type: "bindRepeatableHotkey", - function: () => bigCrunchResetRequest(), + function: () => manualBigCrunchResetRequest(), visible: true }, { name: "Replicanti Galaxy", keys: ["r"], - type: "bindRepeatableHotkey", - function: () => replicantiGalaxyRequest(), + type: "bindHotkey", + function: () => { + replicantiGalaxyRequest(); + setHoldingR(true); + }, visible: () => Replicanti.areUnlocked || PlayerProgress.eternityUnlocked() }, { name: "Eternity", @@ -143,7 +147,6 @@ export const shortcuts = [ keys: ["mod", "s"], type: "bind", function: () => { - if (Pelle.endState >= 4.5) return false; GameStorage.save(false, true); return false; }, @@ -215,25 +218,37 @@ export const shortcuts = [ name: "Change Tab", keys: ["up"], type: "bind", - function: () => keyboardTabChange("up"), + function: () => { + EventHub.dispatch(GAME_EVENT.ARROW_KEY_PRESSED, "up"); + return false; + }, visible: false }, { name: "Change Tab", keys: ["down"], type: "bind", - function: () => keyboardTabChange("down"), + function: () => { + EventHub.dispatch(GAME_EVENT.ARROW_KEY_PRESSED, "down"); + return false; + }, visible: false }, { name: "Change Subtab", keys: ["left"], type: "bind", - function: () => keyboardTabChange("left"), + function: () => { + EventHub.dispatch(GAME_EVENT.ARROW_KEY_PRESSED, "left"); + return false; + }, visible: false }, { name: "Change Subtab", keys: ["right"], type: "bind", - function: () => keyboardTabChange("right"), + function: () => { + EventHub.dispatch(GAME_EVENT.ARROW_KEY_PRESSED, "right"); + return false; + }, visible: false }, { name: "Doesn't exist", @@ -254,9 +269,8 @@ for (const hotkey of shortcuts) { GameKeyboard[hotkey.type](keys, hotkey.function); } -// We need to know whether the player is holding R or not for the -// replicanti galaxy -GameKeyboard.bind("r", () => setHoldingR(true), "keydown"); +// We need to know whether the player is holding R or not for the replicanti galaxy +// The keydown version is above, with the replicantiGalaxyRequest, as otherwise it would be overridden GameKeyboard.bind("r", () => setHoldingR(false), "keyup"); // Same thing with Shift; we need to double-up on ctrl-shift as well since they're technically different keybinds @@ -319,7 +333,7 @@ function toggleBuySingles(buyer) { function keyboardToggleAutobuyers() { Autobuyers.toggle(); - GameUI.notify.info(`Autobuyers ${(player.auto.autobuyersOn) ? "enabled" : "disabled"}`); + GameUI.notify.info(`Autobuyers ${(player.auto.autobuyersOn) ? "resumed" : "paused"}`); } function keyboardToggleContinuum() { @@ -340,10 +354,9 @@ function keyboardAutomatorToggle() { } else { // Only attempt to start the visible script instead of the existing script if it isn't already running const visibleIndex = player.reality.automator.state.editorScript; - const visibleScript = player.reality.automator.scripts[visibleIndex].content; AutomatorBackend.restart(); AutomatorBackend.start(visibleIndex); - if (AutomatorData.currentErrors(AutomatorData.currentScriptText(visibleScript)).length === 0) { + if (AutomatorData.currentErrors().length === 0) { GameUI.notify.info(`Starting script "${AutomatorBackend.scriptName}"`); } else { GameUI.notify.error(`Cannot start script "${AutomatorBackend.scriptName}" (has errors)`); @@ -372,11 +385,9 @@ function armageddonRequest() { } function keyboardPressEscape() { - if (ui.view.modal.queue.length === 0) { - Tab.options.show(true); - } else { - Modal.hideAll(); - } + if (Quote.isOpen || Quote.isHistoryOpen) Quote.clearAll(); + else if (Modal.isOpen) Modal.hideAll(); + else Tab.options.show(true); } function keyboardPressQuestionMark() { @@ -406,22 +417,23 @@ function keyboardVisibleTabsToggle() { Modal.hiddenTabs.show(); } -function keyboardTabChange(direction) { +EventHub.logic.on(GAME_EVENT.ARROW_KEY_PRESSED, direction => { + if (Quote.isOpen || Quote.isHistoryOpen) return; // Current tabs. Defined here as both tab and subtab movements require knowing your current tab. const currentTab = Tabs.current.key; - if (direction === "up" || direction === "down") { + if (direction[0] === "up" || direction[0] === "down") { // Make an array of the keys of all the unlocked and visible tabs const tabs = Tabs.currentUIFormat.flatMap(i => (i.isAvailable ? [i.key] : [])); // Find the index of the tab we are on let top = tabs.indexOf(currentTab); // Move in the desired direction - if (direction === "up") top--; + if (direction[0] === "up") top--; else top++; // Loop around if needed top = (top + tabs.length) % tabs.length; // And now we go there. Tab[tabs[top]].show(true); - } else if (direction === "left" || direction === "right") { + } else if (direction[0] === "left" || direction[0] === "right") { // Current subtabs const currentSubtab = Tabs.current._currentSubtab.key; // Make an array of the keys of all the unlocked and visible subtabs @@ -429,16 +441,14 @@ function keyboardTabChange(direction) { // Find the index of the subtab we are on let sub = subtabs.indexOf(currentSubtab); // Move in the desired direction - if (direction === "left") sub--; + if (direction[0] === "left") sub--; else sub++; // Loop around if needed sub = (sub + subtabs.length) % subtabs.length; // And now we go there. Tab[currentTab][subtabs[sub]].show(true); } - // Return false so the arrow keys don't do anything else - return false; -} +}); const konamiCode = ["up", "up", "down", "down", "left", "right", "left", "right", "b", "a", "enter"]; let konamiStep = 0; diff --git a/javascripts/core/imaginary-upgrades.js b/javascripts/core/imaginary-upgrades.js index e5a394323..221b4a18e 100644 --- a/javascripts/core/imaginary-upgrades.js +++ b/javascripts/core/imaginary-upgrades.js @@ -1,5 +1,5 @@ -import { BitPurchasableMechanicState, RebuyableMechanicState } from "./game-mechanics/index.js"; -import { DC } from "./constants.js"; +import { BitPurchasableMechanicState, RebuyableMechanicState } from "./game-mechanics/index"; +import { DC } from "./constants"; class ImaginaryUpgradeState extends BitPurchasableMechanicState { constructor(config) { @@ -24,7 +24,6 @@ class ImaginaryUpgradeState extends BitPurchasableMechanicState { } get isAvailableForPurchase() { - // eslint-disable-next-line no-bitwise return (player.reality.imaginaryUpgReqs & (1 << this.id)) !== 0; } @@ -33,12 +32,15 @@ class ImaginaryUpgradeState extends BitPurchasableMechanicState { } get canBeApplied() { - return super.canBeApplied && !Pelle.isDisabled("imaginaryUpgrades"); + return super.canBeApplied && !this.pelleDisabled; + } + + get pelleDisabled() { + return Pelle.isDoomed && this.config.isDisabledInDoomed; } tryUnlock() { if (!MachineHandler.isIMUnlocked || this.isAvailableForPurchase || !this.config.checkRequirement()) return; - // eslint-disable-next-line no-bitwise player.reality.imaginaryUpgReqs |= (1 << this.id); GameUI.notify.reality(`You've unlocked an Imaginary Upgrade: ${this.config.name}`); } @@ -71,12 +73,22 @@ class RebuyableImaginaryUpgradeState extends RebuyableMechanicState { } get canBeApplied() { - return super.canBeApplied && !Pelle.isDisabled("imaginaryUpgrades"); + return super.canBeApplied && !this.pelleDisabled; + } + + get pelleDisabled() { + return Pelle.isDoomed; } set boughtAmount(value) { player.reality.imaginaryRebuyables[this.id] = value; } + + onPurchased() { + if (this.id === 7) { + GameCache.staticGlyphWeights.invalidate(); + } + } } ImaginaryUpgradeState.index = mapGameData( @@ -100,7 +112,6 @@ export const ImaginaryUpgrades = { return this.all.countWhere(u => u.isBought); }, get allBought() { - // eslint-disable-next-line no-bitwise return (player.reality.imaginaryUpgradeBits >> 6) + 1 === 1 << (GameDatabase.reality.imaginaryUpgrades.length - 5); } }; diff --git a/javascripts/core/infinity-challenges.js b/javascripts/core/infinity-challenges.js new file mode 100644 index 000000000..f4c11aa07 --- /dev/null +++ b/javascripts/core/infinity-challenges.js @@ -0,0 +1,163 @@ +import { GameMechanicState } from "./game-mechanics/index"; + +export function tryCompleteInfinityChallenges() { + if (EternityMilestone.autoIC.isReached) { + const toComplete = InfinityChallenges.all.filter(x => x.isUnlocked && !x.isCompleted); + for (const challenge of toComplete) challenge.complete(); + } +} + +class InfinityChallengeRewardState extends GameMechanicState { + constructor(config, challenge) { + super(config); + this._challenge = challenge; + } + + get isEffectActive() { + return this._challenge.isCompleted; + } +} + +class InfinityChallengeState extends GameMechanicState { + constructor(config) { + super(config); + this._reward = new InfinityChallengeRewardState(config.reward, this); + } + + get unlockAM() { + return this.config.unlockAM; + } + + get isUnlocked() { + return player.records.thisEternity.maxAM.gte(this.unlockAM) || (Achievement(133).isUnlocked && !Pelle.isDoomed) || + (PelleUpgrade.keepInfinityChallenges.canBeApplied && Pelle.cel.records.totalAntimatter.gte(this.unlockAM)); + } + + get isRunning() { + return player.challenge.infinity.current === this.id; + } + + requestStart() { + if (!this.isUnlocked) return; + if (GameEnd.creditsEverClosed) return; + if (!player.options.confirmations.challenges) { + this.start(); + return; + } + Modal.startInfinityChallenge.show(this.id); + } + + start() { + if (!this.isUnlocked || this.isRunning) return; + player.challenge.normal.current = 0; + player.challenge.infinity.current = this.id; + bigCrunchResetValues(); + if (!Enslaved.isRunning) Tab.dimensions.antimatter.show(); + player.break = true; + if (EternityChallenge.isRunning) Achievement(115).unlock(); + } + + get isCompleted() { + return (player.challenge.infinity.completedBits & (1 << this.id)) !== 0; + } + + complete() { + player.challenge.infinity.completedBits |= 1 << this.id; + EventHub.dispatch(GAME_EVENT.INFINITY_CHALLENGE_COMPLETED); + } + + get isEffectActive() { + return this.isRunning; + } + + /** + * @return {InfinityChallengeRewardState} + */ + get reward() { + return this._reward; + } + + get isQuickResettable() { + return this.config.isQuickResettable; + } + + get goal() { + return this.config.goal; + } + + updateChallengeTime() { + const bestTimes = player.challenge.infinity.bestTimes; + if (bestTimes[this.id - 1] <= player.records.thisInfinity.time) { + return; + } + // TODO: remove splice once player.challenge.infinity.bestTimes is not reactive + bestTimes.splice(this.id - 1, 1, player.records.thisInfinity.time); + GameCache.infinityChallengeTimeSum.invalidate(); + } + + exit() { + player.challenge.infinity.current = 0; + bigCrunchResetValues(); + if (!Enslaved.isRunning) Tab.dimensions.antimatter.show(); + } +} + +/** + * @param {number} id + * @return {InfinityChallengeState} + */ +export const InfinityChallenge = InfinityChallengeState.createAccessor(GameDatabase.challenges.infinity); + +/** + * @returns {InfinityChallengeState} + */ +Object.defineProperty(InfinityChallenge, "current", { + get: () => (player.challenge.infinity.current > 0 + ? InfinityChallenge(player.challenge.infinity.current) + : undefined), +}); + +Object.defineProperty(InfinityChallenge, "isRunning", { + get: () => InfinityChallenge.current !== undefined, +}); + +export const InfinityChallenges = { + /** + * @type {InfinityChallengeState[]} + */ + all: InfinityChallenge.index.compact(), + completeAll() { + for (const challenge of InfinityChallenges.all) challenge.complete(); + }, + clearCompletions() { + player.challenge.infinity.completedBits = 0; + }, + get nextIC() { + return InfinityChallenges.all.find(x => !x.isUnlocked); + }, + get nextICUnlockAM() { + return this.nextIC?.unlockAM; + }, + /** + * Displays a notification if the antimatter gained will surpass the next unlockAM requirement. + * @param value {Decimal} - total antimatter + */ + notifyICUnlock(value) { + // Disable the popup if the user will automatically complete the IC. + if (EternityMilestone.autoIC.isReached) return; + if (InfinityChallenges.nextIC === undefined) return; + for (const ic of InfinityChallenges.all) { + if (ic.isUnlocked || ic.isCompleted) continue; + if (value.lt(ic.unlockAM)) break; + // This has a reasonably high likelihood of happening when the player isn't looking at the game, so + // we leave it there for 5 minutes unless they click it away early + GameUI.notify.infinity(`You have unlocked Infinity Challenge ${ic.id}`, 300000); + } + }, + /** + * @returns {InfinityChallengeState[]} + */ + get completed() { + return InfinityChallenges.all.filter(ic => ic.isCompleted); + } +}; diff --git a/javascripts/core/infinity-upgrades.js b/javascripts/core/infinity-upgrades.js new file mode 100644 index 000000000..65d5ca503 --- /dev/null +++ b/javascripts/core/infinity-upgrades.js @@ -0,0 +1,215 @@ +import { GameMechanicState, SetPurchasableMechanicState } from "./game-mechanics/index"; +import { DC } from "./constants"; + +class ChargedInfinityUpgradeState extends GameMechanicState { + constructor(config, upgrade) { + super(config); + this._upgrade = upgrade; + } + + get isEffectActive() { + return this._upgrade.isBought && this._upgrade.isCharged; + } +} + +export class InfinityUpgradeState extends SetPurchasableMechanicState { + constructor(config) { + super(config); + if (config.charged) { + this._chargedEffect = new ChargedInfinityUpgradeState(config.charged, this); + } + } + + get currency() { + return Currency.infinityPoints; + } + + get set() { + return player.infinityUpgrades; + } + + get isAvailableForPurchase() { + return this.config.checkRequirement?.() ?? true; + } + + get isEffectActive() { + return this.isBought && !this.isCharged; + } + + get chargedEffect() { + return this._chargedEffect; + } + + purchase() { + if (super.purchase()) { + // This applies the 4th column of infinity upgrades retroactively + if (this.config.id.includes("skip")) skipResetsIfPossible(); + EventHub.dispatch(GAME_EVENT.INFINITY_UPGRADE_BOUGHT); + return true; + } + if (this.canCharge) { + this.charge(); + EventHub.dispatch(GAME_EVENT.INFINITY_UPGRADE_CHARGED); + return true; + } + return false; + } + + get hasChargeEffect() { + return this.config.charged !== undefined; + } + + get isCharged() { + return player.celestials.ra.charged.has(this.id); + } + + get canCharge() { + return this.isBought && + this.hasChargeEffect && + !this.isCharged && + Ra.chargesLeft !== 0 && + !Pelle.isDisabled("chargedInfinityUpgrades"); + } + + charge() { + player.celestials.ra.charged.add(this.id); + } + + disCharge() { + player.celestials.ra.charged.delete(this.id); + } +} + +export function totalIPMult() { + if (Effarig.isRunning && Effarig.currentStage === EFFARIG_STAGES.INFINITY) { + return DC.D1; + } + let ipMult = DC.D1 + .times(ShopPurchase.IPPurchases.currentMult) + .timesEffectsOf( + TimeStudy(41), + TimeStudy(51), + TimeStudy(141), + TimeStudy(142), + TimeStudy(143), + Achievement(85), + Achievement(93), + Achievement(116), + Achievement(125), + Achievement(141).effects.ipGain, + InfinityUpgrade.ipMult, + DilationUpgrade.ipMultDT, + GlyphEffect.ipMult + ); + ipMult = ipMult.times(Replicanti.amount.powEffectOf(AlchemyResource.exponential)); + return ipMult; +} + +export function disChargeAll() { + const upgrades = [ + InfinityUpgrade.totalTimeMult, + InfinityUpgrade.dim18mult, + InfinityUpgrade.dim36mult, + InfinityUpgrade.resetBoost, + InfinityUpgrade.buy10Mult, + InfinityUpgrade.dim27mult, + InfinityUpgrade.dim45mult, + InfinityUpgrade.galaxyBoost, + InfinityUpgrade.thisInfinityTimeMult, + InfinityUpgrade.unspentIPMult, + InfinityUpgrade.dimboostMult, + InfinityUpgrade.ipGen + ]; + for (const upgrade of upgrades) { + if (upgrade.isCharged) { + upgrade.disCharge(); + } + } + player.celestials.ra.disCharge = false; + EventHub.dispatch(GAME_EVENT.INFINITY_UPGRADES_DISCHARGED); +} + +// The repeatable 2xIP upgrade has an odd cost structure - it follows a shallow exponential (step *10) up to e3M, at +// which point it follows a steeper one (step *1e10) up to e6M before finally hardcapping. At the hardcap, there's +// an extra bump that increases the multipler itself from e993k to e1M. All these numbers are specified in +// GameDatabase.infinity.upgrades.ipMult +class InfinityIPMultUpgrade extends GameMechanicState { + get cost() { + if (this.purchaseCount >= this.purchasesAtIncrease) { + return this.config.costIncreaseThreshold + .times(Decimal.pow(this.costIncrease, this.purchaseCount - this.purchasesAtIncrease)); + } + return Decimal.pow(this.costIncrease, this.purchaseCount + 1); + } + + get purchaseCount() { + return player.IPMultPurchases; + } + + get purchasesAtIncrease() { + return this.config.costIncreaseThreshold.log10() - 1; + } + + get hasIncreasedCost() { + return this.purchaseCount >= this.purchasesAtIncrease; + } + + get costIncrease() { + return this.hasIncreasedCost ? 1e10 : 10; + } + + get isCapped() { + return this.cost.gte(this.config.costCap); + } + + get isBought() { + return this.isCapped; + } + + get isRequirementSatisfied() { + return Achievement(41).isUnlocked; + } + + get canBeBought() { + return !Pelle.isDoomed && !this.isCapped && Currency.infinityPoints.gte(this.cost) && this.isRequirementSatisfied; + } + + // This is only ever called with amount = 1 or within buyMax under conditions that ensure the scaling doesn't + // change mid-purchase + purchase(amount = 1) { + if (!this.canBeBought) return; + if (!TimeStudy(181).isBought) { + Autobuyer.bigCrunch.bumpAmount(DC.D2.pow(amount)); + } + Currency.infinityPoints.subtract(Decimal.sumGeometricSeries(amount, this.cost, this.costIncrease, 0)); + player.IPMultPurchases += amount; + GameUI.update(); + } + + buyMax() { + if (!this.canBeBought) return; + if (!this.hasIncreasedCost) { + // Only allow IP below the softcap to be used + const availableIP = Currency.infinityPoints.value.clampMax(this.config.costIncreaseThreshold); + const purchases = Decimal.affordGeometricSeries(availableIP, this.cost, this.costIncrease, 0).toNumber(); + if (purchases <= 0) return; + this.purchase(purchases); + } + // Do not replace it with `if else` - it's specifically designed to process two sides of threshold separately + // (for example, we have 1e4000000 IP and no mult - first it will go to (but not including) 1e3000000 and then + // it will go in this part) + if (this.hasIncreasedCost) { + const availableIP = Currency.infinityPoints.value.clampMax(this.config.costCap); + const purchases = Decimal.affordGeometricSeries(availableIP, this.cost, this.costIncrease, 0).toNumber(); + if (purchases <= 0) return; + this.purchase(purchases); + } + } +} + +export const InfinityUpgrade = mapGameDataToObject( + GameDatabase.infinity.upgrades, + config => (config.id === "ipMult" + ? new InfinityIPMultUpgrade(config) + : new InfinityUpgradeState(config)) +); diff --git a/javascripts/core/intervals.js b/javascripts/core/intervals.js index 3b48bc7c5..8b8ec491e 100644 --- a/javascripts/core/intervals.js +++ b/javascripts/core/intervals.js @@ -30,7 +30,10 @@ export const GameIntervals = (function() { // Not a getter because getter will cause stack overflow all() { return Object.values(GameIntervals) - .filter(i => i.hasOwnProperty("start") && i.hasOwnProperty("stop")); + .filter(i => + Object.prototype.hasOwnProperty.call(i, "start") && + Object.prototype.hasOwnProperty.call(i, "stop") + ); }, start() { // eslint-disable-next-line no-shadow @@ -57,19 +60,6 @@ export const GameIntervals = (function() { checkCloudSave: interval(() => { if (player.options.cloudEnabled && Cloud.loggedIn) Cloud.saveCheck(); }, 300000), - submitKongStats: interval(() => { - kong.submitStats("Log10 of total antimatter", player.records.totalAntimatter.e); - kong.submitStats("Log10 of Infinity Points", player.infinityPoints.e); - kong.submitStats("Log10 of Eternity Points", player.eternityPoints.e); - kong.submitStats("NormalChallenge 9 time record (ms)", Math.floor(player.challenge.normal.bestTimes[8])); - kong.submitStats("Fastest Infinity time (ms)", Math.floor(player.records.bestInfinity.time)); - // FIXME: Infinitified is now Decimal so decide what happens here! - // kong.submitStats('Infinitied', Currency.infinitiesTotal); - // FIXME: Eternity count is now a Decimal. - // kong.submitStats("Eternities", Currency.eternities); - kong.submitStats("Achievements", Achievements.effectiveCount + - SecretAchievements.all.countWhere(a => a.isUnlocked)); - }, 60000), randomSecretAchievement: interval(() => { if (Math.random() < 0.00001) SecretAchievement(18).unlock(); }, 1000), @@ -79,7 +69,7 @@ export const GameIntervals = (function() { .then(response => response.json()) .then(json => { if (json.version > player.version) { - Modal.message.show(json.message, updateRefresh); + Modal.message.show(json.message, { callback: updateRefresh }, 3); } }); }, 60000) diff --git a/javascripts/core/keyboard.js b/javascripts/core/keyboard.js index 778717624..c8537f9ff 100644 --- a/javascripts/core/keyboard.js +++ b/javascripts/core/keyboard.js @@ -69,7 +69,7 @@ export class GameKeyboard { GameKeyboard.spins = []; function executeKey(action) { - if (ui.$viewModel.modal.progressBar !== undefined) { + if (ui.$viewModel.modal.progressBar !== undefined || GameEnd.endState >= END_STATE_MARKERS.INTERACTIVITY_DISABLED) { return undefined; } return action(); diff --git a/javascripts/core/kong.js b/javascripts/core/kong.js index 2aee48ba0..b11517cfc 100644 --- a/javascripts/core/kong.js +++ b/javascripts/core/kong.js @@ -1,4 +1,4 @@ -import { RebuyableMechanicState } from "./game-mechanics/index.js"; +import { RebuyableMechanicState } from "./game-mechanics/index"; export const kong = {}; @@ -17,14 +17,6 @@ kong.init = function() { } catch (err) { console.log("Couldn't load Kongregate API"); } }; -kong.submitStats = function(name, value) { - if (!kong.enabled) return; - try { - kongregate.stats.submit(name, value); - // eslint-disable-next-line no-console - } catch (e) { console.log(e); } -}; - class ShopPurchaseState extends RebuyableMechanicState { get currency() { @@ -51,61 +43,84 @@ class ShopPurchaseState extends RebuyableMechanicState { player.IAP[this.config.key] = value; } + get shouldDisplayMult() { + return Boolean(this.config.multiplier); + } + get currentMult() { - return this.config.multiplier(this.purchases); + if (!this.shouldDisplayMult) return ""; + return this.config.multiplier(player.IAP.disabled ? 0 : this.purchases); } get nextMult() { + if (!this.shouldDisplayMult) return ""; + return this.config.multiplier(player.IAP.disabled ? 0 : this.purchases + 1); + } + + // We want to still display the correct value in the button, so we need separate getters for it + get currentMultForDisplay() { + if (!this.shouldDisplayMult) return ""; + return this.config.multiplier(this.purchases); + } + + get nextMultForDisplay() { + if (!this.shouldDisplayMult) return ""; return this.config.multiplier(this.purchases + 1); } + formatEffect(effect) { + return this.config.formatEffect?.(effect) || formatX(effect, 2, 0); + } + purchase() { if (!this.canBeBought) return false; + if (GameEnd.creditsEverClosed) return false; + if (this.config.singleUse && ui.$viewModel.modal.progressBar) return false; player.IAP.spentSTD += this.cost; - this.purchases++; + if (this.config.singleUse) { + this.config.onPurchase(); + } else { + this.purchases++; + } GameUI.update(); return true; } } -export const ShopPurchase = (function() { - const db = GameDatabase.shopPurchases; - return { - dimPurchases: new ShopPurchaseState(db.dimPurchases), - IPPurchases: new ShopPurchaseState(db.IPPurchases), - EPPurchases: new ShopPurchaseState(db.EPPurchases), - allDimPurchases: new ShopPurchaseState(db.allDimPurchases) - }; -}()); +export const ShopPurchase = mapGameDataToObject( + GameDatabase.shopPurchases, + config => new ShopPurchaseState(config) +); -ShopPurchase.all = Object.values(ShopPurchase); +ShopPurchase.respecAll = function() { + for (const purchase of ShopPurchase.all) { + if (purchase.config.singleUse) continue; + player.IAP.spentSTD -= purchase.purchases * purchase.cost; + purchase.purchases = 0; + } +}; -kong.purchaseTimeSkip = function(cost) { - if (player.IAP.totalSTD - player.IAP.spentSTD < cost) return; - player.IAP.spentSTD += cost; +ShopPurchase.respecRequest = function() { + if (player.options.confirmations.respecIAP) { + Modal.respecIAP.show(); + } else { + ShopPurchase.respecAll(); + } +}; + +kong.purchaseTimeSkip = function() { simulateTime(3600 * 6); }; -kong.purchaseLongerTimeSkip = function(cost) { - if (player.IAP.totalSTD - player.IAP.spentSTD < cost) return; - player.IAP.spentSTD += cost; +kong.purchaseLongerTimeSkip = function() { simulateTime(3600 * 24); }; -kong.buyMoreSTD = function(STD, kreds) { - if (!kong.enabled) return; - kongregate.mtx.purchaseItems([`${kreds}worthofstd`], result => { - if (result.success) { - player.IAP.totalSTD += STD; - } - }); -}; - kong.updatePurchases = function() { if (!kong.enabled) return; try { - kongregate.mtx.requestUserItemList("", items); - // eslint-disable-next-line no-console + kongregate.mtx.requestUserItemList("", items); + // eslint-disable-next-line no-console } catch (e) { console.error(e); } function items(result) { @@ -114,40 +129,40 @@ kong.updatePurchases = function() { const item = result.data[i]; switch (item.identifier) { case "doublemult": - totalSTD += 30; - break; + totalSTD += 30; + break; case "doubleip": - totalSTD += 40; - break; + totalSTD += 40; + break; case "tripleep": - totalSTD += 50; - break; + totalSTD += 50; + break; case "alldimboost": - totalSTD += 60; - break; + totalSTD += 60; + break; case "20worthofstd": - totalSTD += 20; - break; + totalSTD += 20; + break; case "50worthofstd": - totalSTD += 60; - break; + totalSTD += 60; + break; case "100worthofstd": - totalSTD += 140; - break; + totalSTD += 140; + break; case "200worthofstd": - totalSTD += 300; - break; + totalSTD += 300; + break; case "500worthofstd": - totalSTD += 1000; - break; + totalSTD += 1000; + break; } } @@ -162,41 +177,41 @@ kong.updatePurchases = function() { kong.migratePurchases = function() { if (!kong.enabled) return; try { - kongregate.mtx.requestUserItemList("", items); - // eslint-disable-next-line no-console + kongregate.mtx.requestUserItemList("", items); + // eslint-disable-next-line no-console } catch (e) { console.log(e); } function items(result) { - let ipPurchases = 0; - let dimPurchases = 0; - let epPurchases = 0; - let alldimPurchases = 0; - for (const item of result.data) { - if (item.identifier === "doublemult") { - player.IAP.totalSTD += 30; - player.IAP.spentSTD += 30; - dimPurchases++; - } - if (item.identifier === "doubleip") { - player.IAP.totalSTD += 40; - player.IAP.spentSTD += 40; - ipPurchases++; - } - if (item.identifier === "tripleep") { - player.IAP.totalSTD += 50; - player.IAP.spentSTD += 50; - epPurchases++; - } - if (item.identifier === "alldimboost") { - player.IAP.totalSTD += 60; - player.IAP.spentSTD += 60; - alldimPurchases++; - } - + let ipPurchases = 0; + let dimPurchases = 0; + let epPurchases = 0; + let alldimPurchases = 0; + for (const item of result.data) { + if (item.identifier === "doublemult") { + player.IAP.totalSTD += 30; + player.IAP.spentSTD += 30; + dimPurchases++; } - player.IAP.dimPurchases = dimPurchases; - player.IAP.allDimPurchases = alldimPurchases; - player.IAP.IPPurchases = ipPurchases; - player.IAP.EPPurchases = epPurchases; + if (item.identifier === "doubleip") { + player.IAP.totalSTD += 40; + player.IAP.spentSTD += 40; + ipPurchases++; + } + if (item.identifier === "tripleep") { + player.IAP.totalSTD += 50; + player.IAP.spentSTD += 50; + epPurchases++; + } + if (item.identifier === "alldimboost") { + player.IAP.totalSTD += 60; + player.IAP.spentSTD += 60; + alldimPurchases++; + } + + } + player.IAP.dimPurchases = dimPurchases; + player.IAP.allDimPurchases = alldimPurchases; + player.IAP.IPPurchases = ipPurchases; + player.IAP.EPPurchases = epPurchases; } }; diff --git a/javascripts/core/machines.js b/javascripts/core/machines.js index ec371c829..056e45fa1 100644 --- a/javascripts/core/machines.js +++ b/javascripts/core/machines.js @@ -1,4 +1,4 @@ -import { DC } from "./constants.js"; +import { DC } from "./constants"; export const MachineHandler = { get baseRMCap() { return DC.E1000; }, @@ -12,7 +12,7 @@ export const MachineHandler = { }, get realityMachineMultiplier() { - return Teresa.rmMultiplier * Effects.max(1, PerkShopUpgrade.rmMult) * + return ShopPurchase.RMPurchases.currentMult * Teresa.rmMultiplier * Effects.max(1, PerkShopUpgrade.rmMult) * getAdjustedGlyphEffect("effarigrm") * Achievement(167).effectOrDefault(1); }, @@ -70,4 +70,15 @@ export const MachineHandler = { return (this.currentIMCap - Currency.imaginaryMachines.value) * (1 - Math.pow(2, (-diff / 1000 / this.scaleTimeForIM))); }, + + estimateIMTimer(cost) { + const imCap = this.currentIMCap; + if (imCap <= cost) return Infinity; + const currentIM = Currency.imaginaryMachines.value; + // This is doing log(a, 1/2) - log(b, 1/2) where a is % left to imCap of cost and b is % left to imCap of current + // iM. log(1 - x, 1/2) should be able to estimate the time taken for iM to increase from 0 to imCap * x since every + // fixed interval the difference between current iM to max iM should decrease by a factor of 1/2. + return Math.max(0, Math.log2(imCap / (imCap - cost)) - Math.log2(imCap / (imCap - currentIM))) * + this.scaleTimeForIM; + } }; diff --git a/javascripts/core/math.js b/javascripts/core/math.js index f127c00e8..e460fa003 100644 --- a/javascripts/core/math.js +++ b/javascripts/core/math.js @@ -1,6 +1,7 @@ -import { DC } from "./constants.js"; import { log as lngamma } from "gamma"; +import { DC } from "./constants"; + /* eslint-disable no-use-before-define */ /* eslint-disable max-params */ @@ -515,13 +516,11 @@ window.logFactorial = (function() { /** 32 bit XORSHIFT generator */ window.xorshift32Update = function xorshift32Update(state) { - /* eslint-disable no-bitwise */ /* eslint-disable no-param-reassign */ state ^= state << 13; state ^= state >>> 17; state ^= state << 5; /* eslint-enable no-param-reassign */ - /* eslint-enable no-bitwise */ return state; }; diff --git a/javascripts/core/new-game.js b/javascripts/core/new-game.js index 34a4ee0e9..e31c04608 100644 --- a/javascripts/core/new-game.js +++ b/javascripts/core/new-game.js @@ -1,52 +1,37 @@ import * as ADNotations from "@antimatter-dimensions/notations"; export const NG = { - get current() { - return player.newGame.current; - }, - - get plusRecord() { - return player.newGame.plusRecord; - }, - - get minusRecord() { - return player.newGame.minusRecord; - }, - - get isNegative() { - return this.current < 0; - }, - - get multiplier() { - if (!this.isNegative) return 2 ** this.current; - return 0.8 ** Math.abs(this.current); - }, - - get power() { - if (!this.isNegative) return 1 + this.current * 0.02; - return 0.99 ** Math.abs(this.current); - }, - - startNewGame(i) { + startNewGame() { + GameEnd.creditsClosed = false; + GameEnd.creditsEverClosed = false; const backUpOptions = JSON.stringify(player.options); // This can't be JSONed as it contains sets const secretUnlocks = player.secretUnlocks; - const newGameBackup = JSON.stringify(player.newGame); const secretAchievements = JSON.stringify(player.secretAchievementBits); + // We don't backup the whole player.reality.automator object because it contains "state", + // which could lead to some edge cases where it starts when it shouldn't (ie before it's unlocked) + // It's easier to do something like this to avoid it entirely. + const automatorConstants = JSON.stringify(player.reality.automator.constants); + const automatorScripts = JSON.stringify(player.reality.automator.scripts); + Modal.hideAll(); + Quote.clearAll(); GameStorage.hardReset(); - player.newGame = JSON.parse(newGameBackup); - player.newGame.current = i; - player.newGame.plusRecord = Math.max(player.newGame.plusRecord, i); - player.newGame.minusRecord = Math.min(player.newGame.minusRecord, i); - Pelle.additionalEnd = 0; player.options = JSON.parse(backUpOptions); player.secretUnlocks = secretUnlocks; player.secretAchievementBits = JSON.parse(secretAchievements); + player.reality.automator.constants = JSON.parse(automatorConstants); + player.reality.automator.scripts = JSON.parse(automatorScripts); ui.view.newUI = player.options.newUI; ui.view.news = player.options.news.enabled; Themes.find(player.options.theme).set(); Notations.all.find(n => n.name === player.options.notation).setAsCurrent(); ADNotations.Settings.exponentCommas.show = player.options.commas; - GameStorage.save(); + player.lastUpdate = Date.now(); + // The ending animation ends at 12.5, although the value continues to increase after that. We set it to a bit above + // 12.5 when we start the rollback animation to hide some of the unavoidable lag from all the reset functions + GameEnd.removeAdditionalEnd = true; + GameEnd.additionalEnd = 15; + // Without the delay, this causes the saving (and its notification) to occur during the credits rollback + setTimeout(() => GameStorage.save(), 10000); } -}; \ No newline at end of file +}; diff --git a/javascripts/core/news-ticker.js b/javascripts/core/news-ticker.js index 34c894d62..93c000533 100644 --- a/javascripts/core/news-ticker.js +++ b/javascripts/core/news-ticker.js @@ -22,7 +22,6 @@ export const NewsHandler = { // If the bit array isn't large enough (ie. the numerical ID is the largest we've seen so far by a long shot), then // we pad the array with zeroes until we can fit the new ID in before actually adding it. while (this.BITS_PER_MASK * player.news.seen[type].length <= number) player.news.seen[type].push(0); - // eslint-disable-next-line no-bitwise player.news.seen[type][Math.floor(number / this.BITS_PER_MASK)] |= 1 << (number % this.BITS_PER_MASK); }, @@ -33,7 +32,6 @@ export const NewsHandler = { const bitArray = player.news.seen[type]; if (!bitArray || this.BITS_PER_MASK * bitArray.length < number) return false; - // eslint-disable-next-line no-bitwise return (bitArray[Math.floor(number / this.BITS_PER_MASK)] |= 1 << (number % this.BITS_PER_MASK)) !== 0; }, diff --git a/javascripts/core/challenge.js b/javascripts/core/normal-challenges.js similarity index 50% rename from javascripts/core/challenge.js rename to javascripts/core/normal-challenges.js index 12ac15da2..3fc6cbf50 100644 --- a/javascripts/core/challenge.js +++ b/javascripts/core/normal-challenges.js @@ -1,19 +1,5 @@ -import { GameMechanicState } from "./game-mechanics/index.js"; -import { DC } from "./constants.js"; - -// This function does *not* reset anything. Only call it when you've already -// done all the non-UI stuff. Right now the only UI thing to do is switch to -// the AD tab. -function startChallengeUI() { - if (!Enslaved.isRunning) Tab.dimensions.antimatter.show(); -} - -export function tryCompleteInfinityChallenges() { - if (EternityMilestone.autoIC.isReached) { - const toComplete = InfinityChallenges.all.filter(x => x.isUnlocked && !x.isCompleted); - for (const challenge of toComplete) challenge.complete(); - } -} +import { DC } from "./constants"; +import { GameMechanicState } from "./game-mechanics/index"; export function updateNormalAndInfinityChallenges(diff) { if (NormalChallenge(11).isRunning || InfinityChallenge(6).isRunning) { @@ -25,10 +11,10 @@ export function updateNormalAndInfinityChallenges(diff) { Currency.matter.multiply(Decimal.pow(cappedBase, diff / 20)); } if (Currency.matter.gt(Currency.antimatter.value) && NormalChallenge(11).isRunning && !Player.canCrunch) { - Modal.hideAll(); - Modal.message.show(`Your ${format(Currency.antimatter.value, 2, 2)} antimatter was annhiliated by ` + - `${format(Currency.matter.value, 2, 2)} matter.`); - softReset(0); + const values = [Currency.antimatter.value, Currency.matter.value]; + softReset(0, true, true); + Modal.message.show(`Your ${format(values[0], 2, 2)} antimatter was annihilated + by ${format(values[1], 2, 2)} matter.`, { closeEvent: GAME_EVENT.BIG_CRUNCH_AFTER }, 1); } } @@ -70,12 +56,17 @@ class NormalChallengeState extends GameMechanicState { return Currency.infinitiesTotal.gte(ip); } + get isDisabled() { + return this.config.isDisabledInDoomed && Pelle.isDoomed; + } + get lockedAt() { return GameDatabase.challenges.normal[this.id].lockedAt; } requestStart() { if (!Tab.challenges.isUnlocked) return; + if (GameEnd.creditsEverClosed) return; if (!player.options.confirmations.challenges) { this.start(); return; @@ -91,18 +82,16 @@ class NormalChallengeState extends GameMechanicState { bigCrunchResetValues(); if (Enslaved.isRunning && EternityChallenge(6).isRunning && this.id === 10) { EnslavedProgress.challengeCombo.giveProgress(); - Enslaved.quotes.show(Enslaved.quotes.EC6C10); + Enslaved.quotes.ec6C10.show(); } - startChallengeUI(); + if (!Enslaved.isRunning) Tab.dimensions.antimatter.show(); } get isCompleted() { - // eslint-disable-next-line no-bitwise return (player.challenge.normal.completedBits & (1 << this.id)) !== 0; } complete() { - // eslint-disable-next-line no-bitwise player.challenge.normal.completedBits |= 1 << this.id; // Since breaking infinity maxes even autobuyers that aren't unlocked, // it's possible to get r52 or r53 from completing a challenge @@ -167,142 +156,3 @@ export const NormalChallenges = { player.challenge.normal.completedBits = 0; } }; - -class InfinityChallengeRewardState extends GameMechanicState { - constructor(config, challenge) { - super(config); - this._challenge = challenge; - } - - get isEffectActive() { - return this._challenge.isCompleted; - } -} - -class InfinityChallengeState extends GameMechanicState { - constructor(config) { - super(config); - this._reward = new InfinityChallengeRewardState(config.reward, this); - } - - get unlockAM() { - return this.config.unlockAM; - } - - get isUnlocked() { - return player.records.thisEternity.maxAM.gte(this.unlockAM) || Achievement(133).isUnlocked; - } - - get isRunning() { - return player.challenge.infinity.current === this.id; - } - - requestStart() { - if (!this.isUnlocked) return; - if (!player.options.confirmations.challenges) { - this.start(); - return; - } - Modal.startInfinityChallenge.show(this.id); - } - - start() { - if (!this.isUnlocked || this.isRunning) return; - player.challenge.normal.current = 0; - player.challenge.infinity.current = this.id; - bigCrunchResetValues(); - startChallengeUI(); - player.break = true; - if (EternityChallenge.isRunning) Achievement(115).unlock(); - } - - get isCompleted() { - // eslint-disable-next-line no-bitwise - return (player.challenge.infinity.completedBits & (1 << this.id)) !== 0; - } - - complete() { - // eslint-disable-next-line no-bitwise - player.challenge.infinity.completedBits |= 1 << this.id; - EventHub.dispatch(GAME_EVENT.INFINITY_CHALLENGE_COMPLETED); - } - - get isEffectActive() { - return this.isRunning; - } - - /** - * @return {InfinityChallengeRewardState} - */ - get reward() { - return this._reward; - } - - get isQuickResettable() { - return this.config.isQuickResettable; - } - - get goal() { - return this.config.goal; - } - - updateChallengeTime() { - const bestTimes = player.challenge.infinity.bestTimes; - if (bestTimes[this.id - 1] <= player.records.thisInfinity.time) { - return; - } - // TODO: remove splice once player.challenge.infinity.bestTimes is not reactive - bestTimes.splice(this.id - 1, 1, player.records.thisInfinity.time); - GameCache.infinityChallengeTimeSum.invalidate(); - } - - exit() { - player.challenge.infinity.current = 0; - bigCrunchResetValues(); - if (!Enslaved.isRunning) Tab.dimensions.antimatter.show(); - } -} - -/** - * @param {number} id - * @return {InfinityChallengeState} - */ -export const InfinityChallenge = InfinityChallengeState.createAccessor(GameDatabase.challenges.infinity); - -/** - * @returns {InfinityChallengeState} - */ -Object.defineProperty(InfinityChallenge, "current", { - get: () => (player.challenge.infinity.current > 0 - ? InfinityChallenge(player.challenge.infinity.current) - : undefined), -}); - -Object.defineProperty(InfinityChallenge, "isRunning", { - get: () => InfinityChallenge.current !== undefined, -}); - -export const InfinityChallenges = { - /** - * @type {InfinityChallengeState[]} - */ - all: InfinityChallenge.index.compact(), - completeAll() { - for (const challenge of InfinityChallenges.all) challenge.complete(); - }, - clearCompletions() { - player.challenge.infinity.completedBits = 0; - }, - get nextIC() { - return InfinityChallenges.all.find(x => !x.isUnlocked); - }, - get nextICUnlockAM() { - return this.nextIC?.unlockAM; - }, - /** - * @returns {InfinityChallengeState[]} - */ - get completed() { - return InfinityChallenges.all.filter(ic => ic.isCompleted); - } -}; diff --git a/javascripts/core/payments.js b/javascripts/core/payments.js new file mode 100644 index 000000000..9f6411a56 --- /dev/null +++ b/javascripts/core/payments.js @@ -0,0 +1,91 @@ +const Payments = { + interval: null, + windowReference: null, + init: () => { + // We have unfinished checkouts + if (player.IAP.checkoutSession.id) { + Payments.pollForPurchases(); + } + }, + buyMoreSTD: async STD => { + player.IAP.checkoutSession = { id: true }; + const res = await fetch("https://antimatterdimensionspayments.ew.r.appspot.com/purchase", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ amount: STD }) + }); + const data = await res.json(); + Payments.windowReference = window.open( + data.url, + "antimatterDimensionsPurchase", + "popup,width=500,height=500,left=100,top=100" + ); + player.IAP.checkoutSession = { id: data.id, amount: STD }; + GameStorage.save(); + Payments.pollForPurchases(); + }, + pollForPurchases: () => { + console.log("Polling for purchases..."); + const { id, amount } = player.IAP.checkoutSession; + let pollAmount = 0; + window.onbeforeunload = async() => { + if (!Payments.interval) return; + Payments.windowReference?.close(); + await Payments.cancelPurchase(); + }; + Payments.interval = setInterval(async() => { + pollAmount++; + const statusRes = await fetch( + `https://antimatterdimensionspayments.ew.r.appspot.com/validate?sessionId=${id}` + ); + const { completed, failure } = await statusRes.json(); + + if (completed) { + Payments.windowReference?.close(); + player.IAP.totalSTD += amount; + GameUI.notify.success(`Purchase of ${amount} STDs was successful, thank you for your support! ❤️`, 10000); + Payments.clearInterval(); + player.IAP.checkoutSession = { id: false }; + GameStorage.save(); + Modal.hide(); + } + + + if (failure) { + Payments.windowReference?.close(); + Payments.clearInterval(); + GameUI.notify.error(`Purchase failed!`, 10000); + player.IAP.checkoutSession = { id: false }; + GameStorage.save(); + return; + } + + // 30 minutes of polling is the maximum + if (!completed && (Payments.windowReference?.closed || pollAmount >= 20 * 30)) { + await Payments.cancelPurchase(); + } + }, 3000); + }, + async cancelPurchase() { + Payments.windowReference?.close(); + Payments.clearInterval(); + await fetch("https://antimatterdimensionspayments.ew.r.appspot.com/expire", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ sessionId: player.IAP.checkoutSession.id }) + }); + GameUI.notify.error(`Purchase failed!`, 10000); + player.IAP.checkoutSession = { id: false }; + GameStorage.save(); + }, + clearInterval() { + clearInterval(Payments.interval); + window.onbeforeunload = null; + } +}; + +export default Payments; \ No newline at end of file diff --git a/javascripts/core/performance-stats.js b/javascripts/core/performance-stats.js index 8f12ba35e..42c0f8f09 100644 --- a/javascripts/core/performance-stats.js +++ b/javascripts/core/performance-stats.js @@ -49,7 +49,7 @@ export const PerformanceStats = { function render(rootBlock) { indentLevel++; for (const blockName in rootBlock) { - if (!rootBlock.hasOwnProperty(blockName)) continue; + if (!Object.prototype.hasOwnProperty.call(rootBlock, blockName)) continue; const block = rootBlock[blockName]; const records = block.records; while (records.length > 1 && records.last().timestamp - records.first().timestamp > samplePeriod) { diff --git a/javascripts/core/perks.js b/javascripts/core/perks.js index 6b5e058f1..46028ed84 100644 --- a/javascripts/core/perks.js +++ b/javascripts/core/perks.js @@ -1,4 +1,4 @@ -import { SetPurchasableMechanicState } from "./game-mechanics/index.js"; +import { SetPurchasableMechanicState } from "./game-mechanics/index"; class PerkState extends SetPurchasableMechanicState { constructor(config) { @@ -37,6 +37,10 @@ class PerkState extends SetPurchasableMechanicState { return this.id === 0 || this.connectedPerks.some(p => p.isBought); } + get canBeApplied() { + return this.isBought && !(Pelle.isDoomed && Pelle.uselessPerks.includes(this.id)); + } + initializeConnections() { this.connectedPerks = GameDatabase.reality.perkConnections[this.id].map(id => Perks.find(id)); } @@ -49,61 +53,13 @@ class PerkState extends SetPurchasableMechanicState { } } -export const Perk = (function() { - const db = GameDatabase.reality.perks; - return { - firstPerk: new PerkState(db.firstPerk), - startAM: new PerkState(db.startAM), - startIP1: new PerkState(db.startIP1), - startIP2: new PerkState(db.startIP2), - startEP1: new PerkState(db.startEP1), - startEP2: new PerkState(db.startEP2), - startEP3: new PerkState(db.startEP3), - startTP: new PerkState(db.startTP), - antimatterNoReset: new PerkState(db.antimatterNoReset), - studyPassive: new PerkState(db.studyPassive), - autounlockEU1: new PerkState(db.autounlockEU1), - autounlockEU2: new PerkState(db.autounlockEU2), - autounlockDilation1: new PerkState(db.autounlockDilation1), - autounlockDilation2: new PerkState(db.autounlockDilation2), - autounlockDilation3: new PerkState(db.autounlockDilation3), - autounlockTD: new PerkState(db.autounlockTD), - autounlockReality: new PerkState(db.autounlockReality), - bypassIDAntimatter: new PerkState(db.bypassIDAntimatter), - bypassTGReset: new PerkState(db.bypassTGReset), - bypassECDilation: new PerkState(db.bypassECDilation), - bypassEC1Lock: new PerkState(db.bypassEC1Lock), - bypassEC2Lock: new PerkState(db.bypassEC2Lock), - bypassEC3Lock: new PerkState(db.bypassEC3Lock), - bypassEC5Lock: new PerkState(db.bypassEC5Lock), - autocompleteEC1: new PerkState(db.autocompleteEC1), - autocompleteEC2: new PerkState(db.autocompleteEC2), - autocompleteEC3: new PerkState(db.autocompleteEC3), - studyActiveEP: new PerkState(db.studyActiveEP), - studyIdleEP: new PerkState(db.studyIdleEP), - studyECRequirement: new PerkState(db.studyECRequirement), - studyECBulk: new PerkState(db.studyECBulk), - retroactiveTP1: new PerkState(db.retroactiveTP1), - retroactiveTP2: new PerkState(db.retroactiveTP2), - retroactiveTP3: new PerkState(db.retroactiveTP3), - retroactiveTP4: new PerkState(db.retroactiveTP4), - autobuyerDilation: new PerkState(db.autobuyerDilation), - autobuyerFasterID: new PerkState(db.autobuyerFasterID), - autobuyerFasterReplicanti: new PerkState(db.autobuyerFasterReplicanti), - autobuyerFasterDilation: new PerkState(db.autobuyerFasterDilation), - ttFree: new PerkState(db.ttFree), - ttBuySingle: new PerkState(db.ttBuySingle), - ttBuyMax: new PerkState(db.ttBuyMax), - achievementGroup1: new PerkState(db.achievementGroup1), - achievementGroup2: new PerkState(db.achievementGroup2), - achievementGroup3: new PerkState(db.achievementGroup3), - achievementGroup4: new PerkState(db.achievementGroup4), - achievementGroup5: new PerkState(db.achievementGroup5) - }; -}()); +export const Perk = mapGameDataToObject( + GameDatabase.reality.perks, + config => new PerkState(config) +); export const Perks = { - all: Object.values(Perk), + all: Perk.all, /** * @param {number} id * @returns {PerkState} diff --git a/javascripts/core/player.js b/javascripts/core/player.js index beb2351be..8d524c9f3 100644 --- a/javascripts/core/player.js +++ b/javascripts/core/player.js @@ -1,7 +1,10 @@ -import { GlyphTypes } from "./glyph-effects.js"; -import { AUTOMATOR_MODE, AUTOMATOR_TYPE } from "./automator/automator-backend.js"; -import { DC } from "./constants.js"; +import { AutomatorPanels } from "../../src/components/tabs/automator/AutomatorDocs"; +import { GlyphInfo } from "../../src/components/modals/options/SelectGlyphInfoDropdown"; + +import { AUTOMATOR_MODE, AUTOMATOR_TYPE } from "./automator/automator-backend"; +import { DC } from "./constants"; import { deepmergeAll } from "@/utility/deepmerge"; +import { GlyphTypes } from "./glyph-effects"; // This is actually reassigned when importing saves // eslint-disable-next-line prefer-const @@ -114,47 +117,71 @@ window.player = { multiplier: DC.D2, isActive: true }, - antimatterDims: Array.range(0, 8).map(tier => ({ - isUnlocked: false, - cost: 1, - interval: [500, 600, 700, 800, 900, 1000, 1100, 1200][tier], - bulk: 1, - mode: AUTOBUYER_MODE.BUY_10, + antimatterDims: { + all: Array.range(0, 8).map(tier => ({ + isUnlocked: false, + cost: 1, + interval: [500, 600, 700, 800, 900, 1000, 1100, 1200][tier], + bulk: 1, + mode: AUTOBUYER_MODE.BUY_10, + isActive: true, + lastTick: 0, + isBought: false + })), isActive: true, - lastTick: 0, - isBought: false - })), - infinityDims: Array.range(0, 8).map(() => ({ - isActive: false, - lastTick: 0, - })), - timeDims: Array.range(0, 8).map(() => ({ - isActive: false, - lastTick: 0, - })), + }, + infinityDims: { + all: Array.range(0, 8).map(() => ({ + isActive: false, + lastTick: 0, + })), + isActive: true, + }, + timeDims: { + all: Array.range(0, 8).map(() => ({ + isActive: false, + lastTick: 0, + })), + isActive: true, + }, replicantiGalaxies: { isActive: false, }, - replicantiUpgrades: Array.range(0, 3).map(() => ({ - isActive: false, - lastTick: 0, - })), + replicantiUpgrades: { + all: Array.range(0, 3).map(() => ({ + isActive: false, + lastTick: 0, + })), + isActive: true, + }, timeTheorems: { isActive: false, }, - dilationUpgrades: Array.range(0, 3).map(() => ({ - isActive: false, - lastTick: 0, - })), - blackHolePower: Array.range(0, 2).map(() => ({ - isActive: false, - })), - realityUpgrades: Array.range(0, 5).map(() => ({ - isActive: false, - })), - imaginaryUpgrades: Array.range(0, 10).map(() => ({ - isActive: false, - })), + dilationUpgrades: { + all: Array.range(0, 3).map(() => ({ + isActive: false, + lastTick: 0, + })), + isActive: true, + }, + blackHolePower: { + all: Array.range(0, 2).map(() => ({ + isActive: false, + })), + isActive: true, + }, + realityUpgrades: { + all: Array.range(0, 5).map(() => ({ + isActive: false, + })), + isActive: true, + }, + imaginaryUpgrades: { + all: Array.range(0, 10).map(() => ({ + isActive: false, + })), + isActive: true, + }, darkMatterDims: { isActive: false, lastTick: 0, @@ -460,6 +487,7 @@ window.player = { autoSort: 0, autoCollapse: false, autoAutoClean: false, + applyFilterToPurge: false, moveGlyphsOnProtection: false, perkPoints: 0, autoEC: true, @@ -479,10 +507,11 @@ window.player = { }, scripts: { }, + constants: {}, execTimer: 0, type: AUTOMATOR_TYPE.BLOCK, forceUnlock: false, - currentInfoPane: 4 + currentInfoPane: AutomatorPanels.INTRO_PAGE, }, achTimer: 0, }, @@ -503,7 +532,7 @@ window.player = { celestials: { teresa: { pouredAmount: 0, - quotes: [], + quoteBits: 0, unlockBits: 0, run: false, bestRunAM: DC.D1, @@ -513,9 +542,9 @@ window.player = { }, effarig: { relicShards: 0, - unlocksBits: 0, + unlockBits: 0, run: false, - quotes: [], + quoteBits: 0, glyphWeights: { ep: 25, repl: 25, @@ -544,11 +573,12 @@ window.player = { autoStoreReal: false, isAutoReleasing: false, storedFraction: 1, - quotes: [], + quoteBits: 0, unlocks: [], run: false, completed: false, tesseracts: 0, + hasSecretStudy: false, feltEternity: false, progressBits: 0, hintBits: 0, @@ -559,7 +589,7 @@ window.player = { v: { unlockBits: 0, run: false, - quotes: [], + quoteBits: 0, runUnlocks: [0, 0, 0, 0, 0, 0, 0, 0, 0], goalReductionSteps: [0, 0, 0, 0, 0, 0, 0, 0, 0], STSpent: 0, @@ -612,20 +642,20 @@ window.player = { dilation: 0, effarig: 0 }, - quotes: [], + quoteBits: 0, momentumTime: 0, - unlocksBits: 0, + unlockBits: 0, run: false, charged: new Set(), disCharge: false, peakGamespeed: 1, - petWithRecollection: "" + petWithRemembrance: "" }, laitela: { darkMatter: DC.D0, maxDarkMatter: DC.D0, run: false, - quotes: [], + quoteBits: 0, dimensions: Array.range(0, 4).map(() => ({ amount: DC.D0, @@ -657,14 +687,11 @@ window.player = { upgrades: new Set(), remnants: 0, realityShards: DC.D0, - // For recording which ones you permanently have - armageddonDuration: 0, records: { totalAntimatter: DC.D0, totalInfinityPoints: DC.D0, totalEternityPoints: DC.D0, }, - maxAMThisArmageddon: DC.D0, rebuyables: { antimatterDimensionMult: 0, timeSpeedMult: 0, @@ -678,12 +705,12 @@ window.player = { galaxyGeneratorEPMult: 0, }, rifts: { - famine: { + vacuum: { fill: DC.D0, active: false, reducedTo: 1 }, - pestilence: { + decay: { fill: DC.D0, active: false, percentageSpent: 0, @@ -694,12 +721,12 @@ window.player = { active: false, reducedTo: 1 }, - war: { + recursion: { fill: DC.D0, active: false, reducedTo: 1 }, - death: { + paradox: { fill: DC.D0, active: false, reducedTo: 1 @@ -713,7 +740,7 @@ window.player = { phase: 0, sacrificeActive: false }, - quotes: [], + quoteBits: 0, collapsed: { upgrades: false, rifts: false, @@ -722,11 +749,7 @@ window.player = { showBought: false, } }, - newGame: { - current: 0, - plusRecord: 0, - minusRecord: 0, - }, + isGameEnd: false, tabNotifications: new Set(), triggeredTabNotificationBits: 0, tutorialState: 0, @@ -749,7 +772,6 @@ window.player = { commas: true, updateRate: 33, newUI: true, - sidebarMinimized: false, offlineProgress: true, automaticTabSwitching: true, respecIntoProtected: false, @@ -757,6 +779,7 @@ window.player = { showLastTenResourceGain: true, autosaveInterval: 30000, showTimeSinceSave: true, + saveFileName: "", exportedFileCount: 0, hideCompletedAchievementRows: false, glyphTextColors: true, @@ -766,6 +789,7 @@ window.player = { ignoreGlyphEffects: false, ignoreGlyphLevel: false, ignoreGlyphRarity: false, + forceDarkGlyphs: true, showHintText: { achievements: true, achievementUnlockStates: true, @@ -775,13 +799,8 @@ window.player = { realityUpgrades: true, perks: true, alchemy: true, - }, - chart: { - updateRate: 1000, - duration: 10, - warning: 0, - on: false, - dips: true + glyphInfoType: GlyphInfo.types.NONE, + showGlyphInfoByDefault: false, }, animations: { bigCrunch: true, @@ -810,7 +829,9 @@ window.player = { bigCrunch: true, replicantiGalaxy: true, antimatterGalaxy: true, - dimensionBoost: true + dimensionBoost: true, + switchAutomatorMode: true, + respecIAP: true }, awayProgress: { antimatter: true, @@ -856,11 +877,16 @@ window.player = { spentSTD: 0, IPPurchases: 0, EPPurchases: 0, + RMPurchases: 0, dimPurchases: 0, - allDimPurchases: 0 + allDimPurchases: 0, + replicantiPurchases: 0, + dilatedTimePurchases: 0, + disabled: false, + checkoutSession: { + id: false, + } }, - // TODO: Remove everything with devMode in it, we (probably?) don't want this in release - devMode: false, }; export const Player = { @@ -983,7 +1009,7 @@ export function guardFromNaNValues(obj) { } for (const key in obj) { - if (!obj.hasOwnProperty(key)) continue; + if (!Object.prototype.hasOwnProperty.call(obj, key)) continue; // TODO: rework autobuyer saving if (key === "automator") continue; diff --git a/javascripts/core/reality-upgrades.js b/javascripts/core/reality-upgrades.js index 144ad447a..5b0093b68 100644 --- a/javascripts/core/reality-upgrades.js +++ b/javascripts/core/reality-upgrades.js @@ -1,4 +1,4 @@ -import { BitPurchasableMechanicState, RebuyableMechanicState } from "./game-mechanics/index.js"; +import { BitPurchasableMechanicState, RebuyableMechanicState } from "./game-mechanics/index"; class RealityUpgradeState extends BitPurchasableMechanicState { constructor(config) { @@ -35,7 +35,6 @@ class RealityUpgradeState extends BitPurchasableMechanicState { } get isAvailableForPurchase() { - // eslint-disable-next-line no-bitwise return (player.reality.upgReqs & (1 << this.id)) !== 0; } @@ -46,7 +45,6 @@ class RealityUpgradeState extends BitPurchasableMechanicState { tryUnlock() { const realityReached = PlayerProgress.realityUnlocked() || TimeStudy.reality.isBought; if (!realityReached || this.isAvailableForPurchase || !this.config.checkRequirement()) return; - // eslint-disable-next-line no-bitwise player.reality.upgReqs |= (1 << this.id); GameUI.notify.reality(`You've unlocked a Reality Upgrade: ${this.config.name}`); } @@ -65,6 +63,7 @@ class RealityUpgradeState extends BitPurchasableMechanicState { if (id === 20 && player.blackHole[0].unlocked) { player.blackHole[1].unlocked = true; } + GameCache.staticGlyphWeights.invalidate(); } } @@ -101,7 +100,6 @@ export const RealityUpgrades = { */ all: RealityUpgradeState.index.compact(), get allBought() { - // eslint-disable-next-line no-bitwise return (player.reality.upgradeBits >> 6) + 1 === 1 << (GameDatabase.reality.upgrades.length - 5); } }; diff --git a/javascripts/core/reality.js b/javascripts/core/reality.js index 9fbe0706d..d0c9e1aad 100644 --- a/javascripts/core/reality.js +++ b/javascripts/core/reality.js @@ -1,4 +1,4 @@ -import { DC } from "./constants.js"; +import { DC } from "./constants"; /** * Object that manages the selection of glyphs offered to the player @@ -13,7 +13,7 @@ export const GlyphSelection = { get choiceCount() { return Effects.max(1, Perk.firstPerk) * - (Ra.has(RA_UNLOCKS.EXTRA_CHOICES_AND_RELIC_SHARD_RARITY_ALWAYS_MAX) ? 2 : 1); + Ra.unlocks.extraGlyphChoicesAndRelicShardRarityAlwaysMax.effectOrDefault(1); }, glyphUncommonGuarantee(glyphList, rng) { @@ -56,7 +56,6 @@ export const GlyphSelection = { }, generate(count, level = gainedGlyphLevel()) { - EventHub.dispatch(GAME_EVENT.GLYPH_CHOICES_GENERATED); this.glyphs = this.glyphList(count, level, { isChoosingGlyph: true }); }, @@ -108,12 +107,14 @@ export function simulatedRealityCount(advancePartSimCounters) { */ export function requestManualReality() { if (GlyphSelection.active || !isRealityAvailable()) return; + if (GameEnd.creditsEverClosed) return; if (player.options.confirmations.glyphSelection) { Modal.reality.show(); return; } - if (Glyphs.freeInventorySpace === 0) { - Modal.message.show("Inventory cannot hold new glyphs. Delete/sacrifice (shift-click) some glyphs."); + if (GameCache.glyphInventorySpace.value === 0) { + Modal.message.show("Inventory cannot hold new Glyphs. Delete/sacrifice (shift-click) some Glyphs.", + { closeEvent: GAME_EVENT.GLYPHS_CHANGED }); return; } processManualReality(false); @@ -134,13 +135,16 @@ export function processManualReality(sacrifice, glyphID) { // modal showed up and the player decided not to pick anything if (glyphID === undefined) { if (EffarigUnlock.glyphFilter.isUnlocked) { - // If the player has the glyph filter, we apply the filter to the choices instead of picking randomly - let newGlyph = AutoGlyphProcessor.pick(GlyphSelection.glyphs); - if (!AutoGlyphProcessor.wouldKeep(newGlyph) || Glyphs.freeInventorySpace === 0) { + // Note that this code path is eventually followed regardless of the glyph selection popping up - if it did, we + // pass through the option selected there; if it didn't, then we apply the filter. If we don't handle it this + // way, manual realities without the modal will never sacrifice and may give bad glyphs you don't care about + const newGlyph = AutoGlyphProcessor.pick(GlyphSelection.glyphs); + const shouldSacrifice = player.options.confirmations.glyphSelection + ? sacrifice + : !AutoGlyphProcessor.wouldKeep(newGlyph); + if (shouldSacrifice || GameCache.glyphInventorySpace.value === 0) { AutoGlyphProcessor.getRidOfGlyph(newGlyph); - newGlyph = null; - } - if (newGlyph && Glyphs.freeInventorySpace > 0) { + } else { Glyphs.addToInventory(newGlyph); } } else { @@ -180,9 +184,11 @@ function triggerManualReality(realityProps) { export function runRealityAnimation() { document.getElementById("ui").style.userSelect = "none"; - document.getElementById("ui").style.animation = "realize 10s 1"; - document.getElementById("realityanimbg").style.animation = "realizebg 10s 1"; + document.getElementById("ui").style.animation = "a-realize 10s 1"; + document.getElementById("realityanimbg").style.animation = "a-realizebg 10s 1"; document.getElementById("realityanimbg").style.display = "block"; + if (Theme.current().isDark()) document.getElementById("realityanimbg").style.filter = "invert(1)"; + else document.getElementById("realityanimbg").style.filter = ""; setTimeout(() => { document.getElementById("realityanimbg").play(); document.getElementById("realityanimbg").currentTime = 0; @@ -203,7 +209,7 @@ function processAutoGlyph(gainedLevel, rng) { const glyphs = GlyphSelection.glyphList(GlyphSelection.choiceCount, gainedLevel, { rng }); if (EffarigUnlock.glyphFilter.isUnlocked) { newGlyph = AutoGlyphProcessor.pick(glyphs); - if (!AutoGlyphProcessor.wouldKeep(newGlyph) || Glyphs.freeInventorySpace === 0) { + if (!AutoGlyphProcessor.wouldKeep(newGlyph) || GameCache.glyphInventorySpace.value === 0) { AutoGlyphProcessor.getRidOfGlyph(newGlyph); newGlyph = null; } @@ -212,7 +218,7 @@ function processAutoGlyph(gainedLevel, rng) { // so we might as well take the first one. newGlyph = glyphs[0]; } - if (newGlyph && Glyphs.freeInventorySpace > 0) { + if (newGlyph && GameCache.glyphInventorySpace.value > 0) { Glyphs.addToInventory(newGlyph); } } @@ -268,7 +274,7 @@ function giveRealityRewards(realityProps) { realityProps.gainedGlyphLevel.actualLevel, realityAndPPMultiplier); Currency.realities.add(realityAndPPMultiplier); Currency.perkPoints.add(realityAndPPMultiplier); - if (Teresa.has(TERESA_UNLOCKS.EFFARIG)) { + if (TeresaUnlocks.effarig.canBeApplied) { Currency.relicShards.add(realityProps.gainedShards * multiplier); } if (multiplier > 1 && Enslaved.boostReality) { @@ -286,11 +292,12 @@ function giveRealityRewards(realityProps) { const current = Teresa.runRewardMultiplier; const newMultiplier = Teresa.rewardMultiplier(player.antimatter); const isHigher = newMultiplier > current; - Modal.message.show(`You have completed Teresa's Reality! ${isHigher + const modalText = `You have completed Teresa's Reality! ${isHigher ? `Since you gained more Antimatter, you increased your Glyph Sacrifice multiplier from ${format(current, 2, 2)} to ${format(newMultiplier, 2, 2)}` : `You did not gain more Antimatter during this run, so the Glyph Sacrifice multiplier - from Teresa did not increase`}.`); + from Teresa did not increase`}.`; + Modal.message.show(modalText, {}, 2); if (Currency.antimatter.gt(player.celestials.teresa.bestRunAM)) { player.celestials.teresa.bestRunAM = Currency.antimatter.value; player.celestials.teresa.bestAMSet = Glyphs.copyForRecords(Glyphs.active.filter(g => g !== null)); @@ -302,17 +309,17 @@ function giveRealityRewards(realityProps) { player.celestials.teresa.lastRepeatedMachines = player.celestials.teresa.lastRepeatedMachines .clampMin(machineRecord); } - Teresa.quotes.show(Teresa.quotes.COMPLETE_REALITY); + Teresa.quotes.completeReality.show(); } if (Effarig.isRunning && !EffarigUnlock.reality.isUnlocked) { EffarigUnlock.reality.unlock(); - Effarig.quotes.show(Effarig.quotes.COMPLETE_REALITY); + Effarig.quotes.completeReality.show(); } if (Enslaved.isRunning) Enslaved.completeRun(); - if (V.isRunning) V.quotes.show(V.quotes.REALITY_COMPLETE); + if (V.isRunning) V.quotes.realityComplete.show(); } // Due to simulated realities taking a long time in late game, this function might not immediately @@ -392,6 +399,9 @@ export function beginProcessReality(realityProps) { addToStats(glyphSample.totalStats, sacGain); } else { processAutoGlyph(realityProps.gainedGlyphLevel, rng); + // We'd normally run processSortingAfterReality() here, but also sorting after every glyph is extremely intensive + // at this scale and largely useless if autoClean is getting run every time too + if (VUnlocks.autoAutoClean.canBeApplied && player.reality.autoAutoClean) Glyphs.autoClean(); } }; const glyphsToSample = 10000; @@ -410,8 +420,8 @@ export function beginProcessReality(realityProps) { more than ${formatInt(glyphsToSample)} Glyphs remaining will speed up the calculation by automatically sacrificing all the remaining Glyphs you would get. Pressing "Skip Glyphs" will ignore all resources related to Glyphs and stop the simulation after giving all other resources. - ${Ra.has(RA_UNLOCKS.GLYPH_ALCHEMY) ? "Pressing either button to speed up simulation will not update" + - " any resources within Glyph Alchemy." : ""}`, + ${Ra.unlocks.unlockGlyphAlchemy.canBeApplied ? `Pressing either button to speed up + simulation will not update any resources within Glyph Alchemy.` : ""}`, progressName: "Realities", current: doneSoFar, max: glyphsToProcess, @@ -527,7 +537,9 @@ export function finishProcessReality(realityProps) { if (!realityProps.glyphUndo) { Glyphs.clearUndo(); if (player.reality.respec) respecGlyphs(); - if (player.celestials.ra.disCharge) disChargeAll(); + if (player.celestials.ra.disCharge) { + disChargeAll(); + } } if (AutomatorBackend.state.forceRestart) AutomatorBackend.restart(); if (player.options.automatorEvents.clearOnReality) AutomatorData.clearEventLog(); @@ -647,7 +659,7 @@ export function finishProcessReality(realityProps) { ECTimeStudyState.invalidateCachedRequirements(); EventHub.dispatch(GAME_EVENT.REALITY_RESET_AFTER); - if (Teresa.has(TERESA_UNLOCKS.START_EU) && !Pelle.isDoomed) { + if (TeresaUnlocks.startEU.canBeApplied) { for (const id of [1, 2, 3, 4, 5, 6]) player.eternityUpgrades.add(id); } @@ -687,7 +699,7 @@ export function applyRUPG10() { } if (Pelle.isDisabled("rupg10")) return; - player.auto.antimatterDims = player.auto.antimatterDims.map(current => ({ + player.auto.antimatterDims.all = player.auto.antimatterDims.all.map(current => ({ isUnlocked: true, // These costs are approximately right; if bought manually all dimensions are slightly different from one another cost: 1e14, @@ -722,13 +734,16 @@ export function clearCelestialRuns() { }; player.celestials.teresa.run = false; player.celestials.effarig.run = false; - // Enslaved forces all tabs to be visible, but exiting via the header might leave the player on a tab which is - // otherwise normally hidden - in that case we force them to the Enslaved tab. We could scan for the lowest-index tab - // and subtab, but all other things being equal the Enslaved tab makes the most sense. The run flag is toggled - // *before* the check because otherwise isHidden will always evaluate to false due to still being in Enslaved. + // Nameless forces all tabs to be visible, but exiting via the header might leave the player on a tab which is + // otherwise normally hidden - in that case we force them to the Nameless tab. We could scan for the lowest-index tab + // and subtab, but all other things being equal the Nameless tab makes the most sense. The run flag is toggled + // *before* the check because otherwise isHidden will always evaluate to false due to still being in Nameless. if (Enslaved.isRunning) { player.celestials.enslaved.run = false; if (Tabs.current.isHidden || Tabs.current._currentSubtab.isHidden) Tab.celestials.enslaved.show(); + // We specifically revalidate here and nowhere else because Nameless changes the unlock state of the BLACK HOLE + // command, which changes the validity of existing scripts when entering/exiting + AutomatorData.recalculateErrors(); } player.celestials.v.run = false; player.celestials.ra.run = false; diff --git a/javascripts/core/replicanti.js b/javascripts/core/replicanti.js index 204cc0ceb..caffe8e00 100644 --- a/javascripts/core/replicanti.js +++ b/javascripts/core/replicanti.js @@ -1,4 +1,4 @@ -import { DC } from "./constants.js"; +import { DC } from "./constants"; // Slowdown parameters for replicanti growth, interval will increase by scaleFactor for every scaleLog10 // OoM past the cap (default is 308.25 (log10 of 1.8e308), 1.2, Number.MAX_VALUE) @@ -97,8 +97,9 @@ export function getReplicantiInterval(overCapOverride, intervalIn) { } } - interval = interval.div(PelleRifts.pestilence.effectValue); + interval = interval.div(PelleRifts.decay.effectValue); interval = interval.div(Pelle.specialGlyphEffect.replication); + interval = interval.div(ShopPurchase.replicantiPurchases.currentMult); if (Pelle.isDisabled("replicantiIntervalMult")) return new Decimal(interval); @@ -107,7 +108,7 @@ export function getReplicantiInterval(overCapOverride, intervalIn) { TimeStudy(213), RealityUpgrade(2), RealityUpgrade(6), - RealityUpgrade(23) + RealityUpgrade(23), ); interval = Decimal.divide(interval, preCelestialEffects); if (TimeStudy(132).isBought && Perk.studyPassive.isBought) { @@ -120,8 +121,7 @@ export function getReplicantiInterval(overCapOverride, intervalIn) { interval = interval.divide(getAdjustedGlyphEffect("replicationspeed")); if (GlyphAlteration.isAdded("replication")) interval = interval.divide( Math.clampMin(Decimal.log10(Replicanti.amount) * getSecondaryGlyphEffect("replicationdtgain"), 1)); - interval = interval.divide(RA_UNLOCKS.TT_BOOST.effect.replicanti()); - interval = interval.dividedByEffectOf(AlchemyResource.replication); + interval = interval.dividedByEffectsOf(AlchemyResource.replication, Ra.unlocks.continuousTTBoost.effects.replicanti); if (V.isRunning) { // This is a boost if interval < 1, but that only happens in EC12 // and handling it would make the replicanti code a lot more complicated. @@ -148,15 +148,29 @@ export function replicantiLoop(diff) { const interval = getReplicantiInterval(false); const isUncapped = Replicanti.isUncapped; const areRGsBeingBought = Replicanti.galaxies.areBeingBought; - if (diff > 500 || interval.lessThan(diff) || isUncapped) { - // Gain code for sufficiently fast or large amounts of replicanti (growth per tick == chance * amount) + + // Figure out how many ticks to calculate for and roll over any leftover time to the next tick. The rollover + // calculation is skipped if there's more than 100 replicanti ticks per game tick to reduce round-off problems. + let tickCount = Decimal.divide(diff + player.replicanti.timer, interval); + if (tickCount.lt(100)) player.replicanti.timer = tickCount.minus(tickCount.floor()).times(interval).toNumber(); + else player.replicanti.timer = 0; + tickCount = tickCount.floor(); + + const singleTickAvg = Replicanti.amount.times(player.replicanti.chance); + // Note that code inside this conditional won't necessarily run every game tick; when game ticks are slower than + // replicanti ticks, then tickCount will look like [0, 0, 0, 1, 0, 0, ...] on successive game ticks + if (tickCount.gte(100) || (singleTickAvg.gte(10) && tickCount.gte(1))) { + // Fast gain: If we're doing a very large number of ticks or each tick produces a lot, then continuous growth + // every replicanti tick is a good approximation and less intensive than distribution samples. This path will + // always happen above 1000 replicanti due to how singleTickAvg is calculated, so the over-cap math is only + // present on this path let postScale = Math.log10(ReplicantiGrowth.scaleFactor) / ReplicantiGrowth.scaleLog10; if (V.isRunning) { postScale *= 2; } // Note that remainingGain is in log10 terms. - let remainingGain = Decimal.divide(diff * Math.log(player.replicanti.chance + 1), interval).times(LOG10_E); + let remainingGain = tickCount.times(Math.log(player.replicanti.chance + 1)).times(LOG10_E); // It is intended to be possible for both of the below conditionals to trigger. if (!isUncapped || Replicanti.amount.lte(replicantiCap())) { // Some of the gain is "used up" below e308, but if replicanti are uncapped @@ -172,16 +186,30 @@ export function replicantiLoop(diff) { Decimal.exp(remainingGain.div(LOG10_E).times(postScale).plus(1).ln() / postScale + Replicanti.amount.clampMin(1).ln()); } - player.replicanti.timer = 0; - } else if (interval.lte(player.replicanti.timer)) { + } else if (tickCount.gt(1)) { + // Multiple ticks but "slow" gain: This happens at low replicanti chance and amount with a fast interval, which + // can happen often in early cel7. In this case we "batch" ticks together as full doubling events and then draw + // from a Poisson distribution for how many times to do that. Any leftover ticks are used as binomial samples + const batchTicks = Math.floor(tickCount.toNumber() * Math.log2(1 + player.replicanti.chance)); + const binomialTicks = tickCount.toNumber() - batchTicks / Math.log2(1 + player.replicanti.chance); + + Replicanti.amount = Replicanti.amount.times(DC.D2.pow(poissonDistribution(batchTicks))); + for (let t = 0; t < Math.floor(binomialTicks); t++) { + const reproduced = binomialDistribution(Replicanti.amount, player.replicanti.chance); + Replicanti.amount = Replicanti.amount.plus(reproduced); + } + + // The batching might use partial ticks; we add the rest back to the timer so it gets used next loop + const leftover = binomialTicks - Math.floor(binomialTicks); + player.replicanti.timer += interval.times(leftover).toNumber(); + } else if (tickCount.eq(1)) { + // Single tick: Take a single binomial sample to properly simulate replicanti growth with randomness const reproduced = binomialDistribution(Replicanti.amount, player.replicanti.chance); Replicanti.amount = Replicanti.amount.plus(reproduced); - if (!isUncapped) Replicanti.amount = Decimal.min(replicantiCap(), Replicanti.amount); - player.replicanti.timer += diff - interval.toNumber(); - } else { - player.replicanti.timer += diff; } + if (!isUncapped) Replicanti.amount = Decimal.min(replicantiCap(), Replicanti.amount); + if (Pelle.isDoomed && Replicanti.amount.log10() - replicantiBeforeLoop.log10() > 308) { Replicanti.amount = replicantiBeforeLoop.times(1e308); } @@ -266,7 +294,7 @@ export const ReplicantiUpgrade = { } get cost() { - return player.replicanti.chanceCost.dividedByEffectOf(PelleRifts.famine.milestones[1]); + return player.replicanti.chanceCost.dividedByEffectOf(PelleRifts.vacuum.milestones[1]); } get baseCost() { return player.replicanti.chanceCost; } @@ -317,7 +345,7 @@ export const ReplicantiUpgrade = { } get cost() { - return player.replicanti.intervalCost.dividedByEffectOf(PelleRifts.famine.milestones[1]); + return player.replicanti.intervalCost.dividedByEffectOf(PelleRifts.vacuum.milestones[1]); } get baseCost() { return player.replicanti.intervalCost; } @@ -352,7 +380,7 @@ export const ReplicantiUpgrade = { } get cost() { - return this.baseCost.dividedByEffectsOf(TimeStudy(233), PelleRifts.famine.milestones[1]); + return this.baseCost.dividedByEffectsOf(TimeStudy(233), PelleRifts.vacuum.milestones[1]); } get baseCost() { return player.replicanti.galCost; } @@ -385,7 +413,7 @@ export const ReplicantiUpgrade = { } get extra() { - return Effects.max(0, TimeStudy(131)) + PelleRifts.pestilence.milestones[2].effectOrDefault(0); + return Effects.max(0, TimeStudy(131)) + PelleRifts.decay.milestones[2].effectOrDefault(0); } autobuyerTick() { @@ -447,7 +475,7 @@ export const Replicanti = { }; }, unlock(freeUnlock = false) { - const cost = DC.E140.dividedByEffectOf(PelleRifts.famine.milestones[1]); + const cost = DC.E140.dividedByEffectOf(PelleRifts.vacuum.milestones[1]); if (player.replicanti.unl) return; if (freeUnlock || Currency.infinityPoints.gte(cost)) { if (!freeUnlock) Currency.infinityPoints.subtract(cost); @@ -488,7 +516,9 @@ export const Replicanti = { }, get areBeingBought() { const buyer = Autobuyer.replicantiGalaxy; - return (buyer.canTick && buyer.isEnabled) || this.isPlayerHoldingR; + // If the confirmation is enabled, we presume the player wants to confirm each Replicanti Galaxy purchase + return (buyer.canTick && buyer.isEnabled) || + (!player.options.confirmations.replicantiGalaxy && this.isPlayerHoldingR); }, get gain() { if (!this.canBuyMore) return 0; @@ -501,6 +531,6 @@ export const Replicanti = { }, }, get isUncapped() { - return TimeStudy(192).isBought || PelleRifts.famine.milestones[1].canBeApplied; + return TimeStudy(192).isBought || PelleRifts.vacuum.milestones[1].canBeApplied; } }; diff --git a/javascripts/core/sacrifice.js b/javascripts/core/sacrifice.js index 80d2e8615..bbacd5829 100644 --- a/javascripts/core/sacrifice.js +++ b/javascripts/core/sacrifice.js @@ -1,4 +1,4 @@ -import { DC } from "./constants.js"; +import { DC } from "./constants"; export class Sacrifice { // This is tied to the "buying an 8th dimension" achievement in order to hide it from new players before they reach diff --git a/javascripts/core/secret-formula/achievements/normal-achievements.js b/javascripts/core/secret-formula/achievements/normal-achievements.js index b9586ad61..2657e9c55 100644 --- a/javascripts/core/secret-formula/achievements/normal-achievements.js +++ b/javascripts/core/secret-formula/achievements/normal-achievements.js @@ -1,5 +1,5 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; GameDatabase.achievements.normal = [ { @@ -522,8 +522,8 @@ GameDatabase.achievements.normal = [ { id: 78, name: "Blink of an eye", - get description() { return `Get to Infinity in under ${formatInt(200)} milliseconds.`; }, - checkRequirement: () => Time.thisInfinityRealTime.totalMilliseconds <= 200, + get description() { return `Infinity in under ${formatInt(250)}ms.`; }, + checkRequirement: () => Time.thisInfinityRealTime.totalMilliseconds <= 250, checkEvent: GAME_EVENT.BIG_CRUNCH_BEFORE, get reward() { return `Start with ${format(5e25)} antimatter.`; @@ -702,7 +702,7 @@ GameDatabase.achievements.normal = [ id: 102, name: "This mile took an eternity", description: "Get all Eternity milestones.", - checkRequirement: () => EternityMilestones.all.every(m => m.isReached), + checkRequirement: () => EternityMilestone.all.every(m => m.isReached), checkEvent: GAME_EVENT.GAME_TICK_AFTER }, { @@ -782,18 +782,12 @@ GameDatabase.achievements.normal = [ }, { id: 113, - name: "Long lasting relationship", - get description() { - return `Have your Infinity Power per second exceed your Infinity Power - for ${formatInt(60)} consecutive seconds during a single Infinity.`; - }, - checkRequirement: () => AchievementTimers.marathon2 - .check( - !EternityChallenge(7).isRunning && - InfinityDimension(1).productionPerSecond.gt(Currency.infinityPower.value), - 60 - ), - checkEvent: GAME_EVENT.GAME_TICK_AFTER + name: "Eternities are the new infinity", + get description() { return `Eternity in under ${formatInt(250)}ms.`; }, + checkRequirement: () => Time.thisEternity.totalMilliseconds <= 250, + checkEvent: GAME_EVENT.ETERNITY_RESET_BEFORE, + get reward() { return `Gain ${formatX(2)} more Eternities.`; }, + effect: 2, }, { id: 114, @@ -863,10 +857,18 @@ GameDatabase.achievements.normal = [ }, { id: 124, - name: "Eternities are the new infinity", - get description() { return `Eternity in under ${formatInt(200)}ms.`; }, - checkRequirement: () => Time.thisEternity.totalMilliseconds <= 200, - checkEvent: GAME_EVENT.ETERNITY_RESET_BEFORE + name: "Long lasting relationship", + get description() { + return `Have your Infinity Power per second exceed your Infinity Power + for ${formatInt(60)} consecutive seconds during a single Infinity.`; + }, + checkRequirement: () => AchievementTimers.marathon2 + .check( + !EternityChallenge(7).isRunning && + InfinityDimension(1).productionPerSecond.gt(Currency.infinityPower.value), + 60 + ), + checkEvent: GAME_EVENT.GAME_TICK_AFTER }, { id: 125, @@ -950,10 +952,7 @@ GameDatabase.achievements.normal = [ player.IPMultPurchases === 0 && Currency.infinityPoints.exponent >= 200000, checkEvent: GAME_EVENT.GAME_TICK_AFTER, - get reward() { - if (Pelle.isDoomed) return "You start Eternities with all Infinity Challenges unlocked."; - return "You start Eternities with all Infinity Challenges unlocked and completed."; - } + reward: "You start Eternities with all Infinity Challenges unlocked and completed." }, { id: 134, @@ -1027,7 +1026,8 @@ GameDatabase.achievements.normal = [ name: "How does this work?", description: "Unlock the automator.", checkRequirement: () => Player.automatorUnlocked, - checkEvent: GAME_EVENT.REALITY_RESET_AFTER, + checkEvent: [GAME_EVENT.REALITY_RESET_AFTER, GAME_EVENT.REALITY_UPGRADE_BOUGHT, GAME_EVENT.PERK_BOUGHT, + GAME_EVENT.BLACK_HOLE_BOUGHT], get reward() { return `Dimension Boosts are ${formatPercents(0.5)} stronger.`; }, effect: 1.5, }, @@ -1103,7 +1103,7 @@ GameDatabase.achievements.normal = [ }, { id: 152, - name: "Y'all got any more of them glyphs?", + name: "Y'all got any more of them Glyphs?", get description() { return `Have ${formatInt(100)} Glyphs in your inventory.`; }, checkRequirement: () => Glyphs.inventoryList.length >= 100, checkEvent: GAME_EVENT.GLYPHS_CHANGED @@ -1273,7 +1273,9 @@ GameDatabase.achievements.normal = [ get description() { return `Get ${formatInt(Ra.alchemyResourceCap)} of all Alchemy Resources.`; }, checkRequirement: () => AlchemyResources.all.every(x => x.amount >= Ra.alchemyResourceCap), checkEvent: GAME_EVENT.REALITY_RESET_AFTER, - get reward() { return `Momentum increases ${formatX(10)} faster.`; }, + get reward() { + return `Synergism can go above ${formatPercents(1)} and Momentum increases ${formatX(10)} faster.`; + }, effect: 10, }, { @@ -1285,6 +1287,8 @@ GameDatabase.achievements.normal = [ id: 177, name: "This mile took a celestial", description: "Complete all Singularity Milestones at least once.", + checkRequirement: () => SingularityMilestones.all.every(x => x.completions > 0), + checkEvent: GAME_EVENT.SINGULARITY_RESET_AFTER, }, { id: 178, @@ -1306,8 +1310,9 @@ GameDatabase.achievements.normal = [ { id: 182, name: "One more time", - description: "Gain back all Antimatter Dimension autobuyers.", - checkRequirement: () => player.celestials.pelle.upgrades.has(4), + description: "Permanently gain back all Antimatter Dimension autobuyers.", + checkRequirement: () => PelleUpgrade.antimatterDimAutobuyers1.canBeApplied && + PelleUpgrade.antimatterDimAutobuyers2.canBeApplied, checkEvent: GAME_EVENT.GAME_TICK_AFTER }, { @@ -1344,15 +1349,22 @@ GameDatabase.achievements.normal = [ { id: 187, name: "The One with Dilated Time", - description: "Unlock Dilation in Doomed.", + description: "Unlock Dilation while Doomed.", checkRequirement: () => PelleStrikes.dilation.hasStrike, - checkEvent: GAME_EVENT.PELLE_STRIKE_UNLOCKED + checkEvent: GAME_EVENT.PELLE_STRIKE_UNLOCKED, + // We forgot to disable a singularity milestone while balancing Pelle; now it's disabled + // and this upgrade has the same effect as it used to. + get reward() { + return `Increase the multiplier per repeatable Dilated Time + multiplier upgrade by ${formatX(1.35, 0, 2)}.`; + }, + effect: 1.35 }, { id: 188, name: "The End", description: "Beat the game.", - checkRequirement: () => Pelle.endState > 1, + checkRequirement: () => GameEnd.endState > END_STATE_MARKERS.GAME_END && !GameEnd.removeAdditionalEnd, checkEvent: GAME_EVENT.GAME_TICK_AFTER }, ]; diff --git a/javascripts/core/secret-formula/achievements/secret-achievements.js b/javascripts/core/secret-formula/achievements/secret-achievements.js index 567a52efd..0bb9ee9b8 100644 --- a/javascripts/core/secret-formula/achievements/secret-achievements.js +++ b/javascripts/core/secret-formula/achievements/secret-achievements.js @@ -1,4 +1,4 @@ -import { GameDatabase } from "../game-database.js"; +import { GameDatabase } from "../game-database"; GameDatabase.achievements.secret = [ { @@ -120,7 +120,7 @@ GameDatabase.achievements.secret = [ { id: 33, name: "A sound financial decision", - description: "Click on the donate link." + description: "Click on the button to purchase STD coins." }, { id: 34, @@ -146,8 +146,8 @@ GameDatabase.achievements.secret = [ }, { id: 38, - name: "Professional bodybuilder", - description: "This one is getting replaced due to a recent PR." + name: "Knife's edge", + description: "Close the Hard Reset modal after typing in the confirmation." }, { id: 41, @@ -161,10 +161,10 @@ GameDatabase.achievements.secret = [ }, { id: 43, - name: "Time fixes everything", - description: "Fix infinity while Dilated.", - checkRequirement: () => player.dilation.active, - checkEvent: GAME_EVENT.FIX_INFINITY + name: "A cacophonous chorus", + description: "Have all equipped Glyphs be Music Glyphs.", + checkRequirement: () => Glyphs.active.length && Glyphs.active.every(x => x?.symbol === "key266b"), + checkEvent: GAME_EVENT.GLYPHS_EQUIPPED_CHANGED }, { id: 44, @@ -181,17 +181,17 @@ GameDatabase.achievements.secret = [ }, { id: 46, - name: "s46", - description: "s46" + name: "For a rainy day", + description: "Store a day of real time." }, { id: 47, - name: "s47", - description: "s47" + name: "ALT+", + description: "Hide every possible tab." }, { id: 48, - name: "s48", - description: "s48" + name: "Stack overflow", + description: "Have more Automator errors than lines." }, ]; diff --git a/javascripts/core/secret-formula/away-progress-types.js b/javascripts/core/secret-formula/away-progress-types.js index 92f3b0e0e..e6d3f0cca 100644 --- a/javascripts/core/secret-formula/away-progress-types.js +++ b/javascripts/core/secret-formula/away-progress-types.js @@ -1,4 +1,4 @@ -import { GameDatabase } from "./game-database.js"; +import { GameDatabase } from "./game-database"; GameDatabase.awayProgressTypes = [ { @@ -77,10 +77,10 @@ GameDatabase.awayProgressTypes = [ }, { name: "relicShards", reference: ["celestials", "effarig", "relicShards"], - isUnlocked: () => Teresa.has(TERESA_UNLOCKS.EFFARIG), + isUnlocked: () => TeresaUnlocks.effarig.canBeApplied, }, { name: "celestialMemories", - isUnlocked: () => V.has(V_UNLOCKS.RA_UNLOCK), + isUnlocked: () => VUnlocks.raUnlock.isUnlocked, // Functions as the visible option for all Memories, never appears due to having no reference. appearsInAwayModal: false, }, { diff --git a/javascripts/core/secret-formula/catchup-resources.js b/javascripts/core/secret-formula/catchup-resources.js new file mode 100644 index 000000000..f23967b8e --- /dev/null +++ b/javascripts/core/secret-formula/catchup-resources.js @@ -0,0 +1,308 @@ +import { DC } from "../constants"; + +import { GameDatabase } from "./game-database"; + +GameDatabase.catchupResources = [ + { + name: "Antimatter Dimensions", + requiredStage: PROGRESS_STAGE.PRE_INFINITY, + description: `Every Antimatter Dimension continuously produces Dimensions of the next tier down. The lowest + Antimatter Dimension produces antimatter.` + }, + { + name: "Tickspeed Upgrades", + requiredStage: PROGRESS_STAGE.PRE_INFINITY, + description: `Tickspeed Upgrades make Antimatter Dimensions produce other Antimatter Dimensions or antimatter + as if time were passing faster.` + }, + { + name: "Autobuyers", + requiredStage: PROGRESS_STAGE.PRE_INFINITY, + description: `Autobuyers are a built-in feature to the game which purchases upgrades for your Antimatter + Dimensions automatically when you can afford them.` + }, + { + name: "Dimension Boosts", + requiredStage: PROGRESS_STAGE.PRE_INFINITY, + description: `Dimension Boosts are gained by resetting all your Antimatter Dimensions and tickspeed after + reaching a certain amount of the highest available Antimatter Dimension. They provide a multiplier to your + Antimatter Dimensions.` + }, + { + name: "Antimatter Galaxies", + requiredStage: PROGRESS_STAGE.PRE_INFINITY, + description: `Antimatter Galaxies are gained by resetting your Antimatter Dimensions and Dimension Boosts. They + improve the effectiveness of your Tickspeed Upgrades in a compounding way.` + }, + { + name: "Infinity", + requiredStage: PROGRESS_STAGE.EARLY_INFINITY, + description: () => `Infinity is the first main reset layer. Reaching ${format(Number.MAX_VALUE, 2)} antimatter + allows you to reset everything up to this point in exchange for unlocking new content and resources.` + }, + { + name: "Infinity Points", + requiredStage: PROGRESS_STAGE.EARLY_INFINITY, + description: `Infinity Points are the primary resource after completing your first Infinity. They can be spent on + features which persist through Infinity resets.` + }, + { + name: "Challenges", + requiredStage: PROGRESS_STAGE.EARLY_INFINITY, + description: () => `Challenges require you to reach ${format(Number.MAX_VALUE, 2)} antimatter under more difficult + conditions. Completing challenges allows you to upgrade your Autobuyers.` + }, + { + name: "Break Infinity", + requiredStage: PROGRESS_STAGE.BREAK_INFINITY, + description: () => `Upgrading your Big Crunch Autobuyer to the maximum allows you to surpass + ${format(Number.MAX_VALUE, 2)} antimatter, giving increasing amounts of Infinity Points with more antimatter.` + }, + { + name: "Infinity Dimensions", + requiredStage: PROGRESS_STAGE.BREAK_INFINITY, + description: `Infinity Dimensions Produce in a cascading fashion like Antimatter Dimensions. The lowest tier of + Infinity Dimension produces Infinity Power, which applies a large multiplier to all Antimatter Dimensions.` + }, + { + name: "Infinity Challenges", + requiredStage: PROGRESS_STAGE.BREAK_INFINITY, + description: () => `Infinity Challenges are new challenges with an antimatter goal above + ${format(Number.MAX_VALUE, 2)}. Completing them rewards upgrades and production boosts.` + }, + { + name: "Replicanti", + requiredStage: PROGRESS_STAGE.REPLICANTI, + description: () => `Replicanti is a resource which produces itself over time, giving a multiplier to all + Infinity Dimensions. At ${format(Number.MAX_VALUE, 2)} Replicanti, they can be reset to ${formatInt(1)} for an + additional Galaxy which does not increase the cost of Antimatter Galaxies. They also reset after every Infinity.` + }, + { + name: "Eternity", + requiredStage: PROGRESS_STAGE.EARLY_ETERNITY, + description: () => `Eternity is the second main reset layer. Reaching ${format(Number.MAX_VALUE, 2)} Infinity Points + allows you to reset everything up to this point for access to new content and resources.` + }, + { + name: "Eternity Points", + requiredStage: PROGRESS_STAGE.EARLY_ETERNITY, + description: `Infinity Points are the primary resource after completing your first Eternity, and scale based on your + Infinity Points at the time you complete the Eternity.` + }, + { + name: "Time Studies", + requiredStage: PROGRESS_STAGE.EARLY_ETERNITY, + description: `Time Studies are a set of upgrades akin to a skill tree, and can be freely re-allocated after every + Eternity with no resource loss. Some sections of the tree have restrictions which forbid you from choosing + particular studies simultaneously.` + }, + { + name: "Eternity Milestones", + requiredStage: PROGRESS_STAGE.EARLY_ETERNITY, + description: `Eternity Milestones are forms of built-in automation and convenience which are unlocked simply by + completing more Eternities. Unlocking them does not require spending any resources.` + }, + { + name: "Time Dimensions", + requiredStage: PROGRESS_STAGE.EARLY_ETERNITY, + description: `Time Dimensions also produce each other in a cascading manner, with the lowest tier producing Time + Shards. Time Shards give you additional Tickspeed Upgrades which do not increase the cost of the Tickspeed + Upgrades purchased with antimatter.` + }, + { + name: "Eternity Challenges", + requiredStage: PROGRESS_STAGE.ETERNITY_CHALLENGES, + description: `Eternity Challenges are modified Eternities with an Infinity Point goal which must be reached for + completion. They can be completed up to five times, getting more difficult each repetition in exchange for + increasingly powerful rewards.` + }, + { + name: "Time Dilation", + requiredStage: PROGRESS_STAGE.EARLY_DILATION, + description: () => `Time Dilation is a modified Eternity where tickspeed and all Dimension multipliers are + severely reduced. Completing Dilated Eternities gives Tachyon Particles.` + }, + { + name: "Tachyon Particles", + requiredStage: PROGRESS_STAGE.EARLY_DILATION, + description: () => `Tachyon Particles are a resource which cannot be farmed and require you to get a higher amount + of antimatter in a Dilated Eternity in order to increase your amount. Tachyon Particles produce Dilated Time.` + }, + { + name: "Reality", + requiredStage: PROGRESS_STAGE.EARLY_REALITY, + description: () => `Reality is the third and final main reset layer. Reaching ${format(DC.E4000)} Eternity Points + gives you the option to reset everything up to this point in exchange for unlocking new content and gaining + access to new resources.` + }, + { + name: "Reality Machines", + requiredStage: PROGRESS_STAGE.EARLY_REALITY, + description: `Reality Machines are the primary resource after completing your first Reality. They are given based + on Eternity Points at the time of completing a Reality.` + }, + { + name: "Perks", + requiredStage: PROGRESS_STAGE.EARLY_REALITY, + description: `Perks are unlockable features similar to Eternity Milestones which primarily focus on convenience and + automation. They are purchased using Perk Points, which are gained after every Reality.` + }, + { + name: "Glyphs", + requiredStage: PROGRESS_STAGE.EARLY_REALITY, + description: `Glyphs are equippable upgrades which can only be unequipped between Realities. Every Reality you are + allowed to choose one of multiple new random Glyphs to receive; the average quality of your available choices + is determined by how high some of your resources reached in that Reality.` + }, + { + name: "Automator", + requiredStage: PROGRESS_STAGE.EARLY_REALITY, + description: `The Automator is a built-in feature that uses a scripting language that allows you to eventually + finish Realities completely hands-off with enough upgrades and perks.` + }, + { + name: "Black Hole", + requiredStage: PROGRESS_STAGE.EARLY_REALITY, + description: `The Black Hole runs the entire game faster in a periodic cycle. This affects everything in the game up + to this point and will give similar results to leaving the game open for an equivalent amount of time.` + }, + { + name: "Teresa", + requiredStage: PROGRESS_STAGE.TERESA, + description: `Teresa is the first Celestial, who has a more difficult Reality which gives a massive boost to Glyph + Sacrifice depending upon completion. They unlock upgrades which focus on testing and automating Realities more + easily.` + }, + { + name: "Effarig", + requiredStage: PROGRESS_STAGE.EFFARIG, + description: `Effarig is the second Celestial, whose Reality limits your Glyphs and has scaling nerfs but gives + rewards for each new reset layer reached. They unlock upgrades which focus on automatically selecting and + filtering the large number of Glyphs you are receiving, purchased with a new resource called Relic Shards.` + }, + { + name: "The Nameless Ones", + requiredStage: PROGRESS_STAGE.ENSLAVED, + description: `The Nameless Ones are the third Celestial, whose Reality is extremely punishing with a long list of + nerfs, but unlocks Tesseracts for those who can figure out how to prevail. They also modify your Black Hole to + allow it to store time.` + }, + { + name: "Stored Time", + requiredStage: PROGRESS_STAGE.ENSLAVED, + description: `Your Black Hole has the ability to store time in two ways. Charging it allows you to hold on to + sped-up time and release it later as a single skip-forward burst. Storing real time lets you use actual time + to simulate Realities (giving you the resources of that Reality but multiplied), or as a stand-in for + offline progress.` + }, + { + name: "Tesseracts", + requiredStage: PROGRESS_STAGE.ENSLAVED, + description: `Infinity Dimensions cannot be purchased indefinitely and all but the 8th have a hard limit for how + many times they can be purchased. Each Tesseract permanently increases this limit by a large amount.` + }, + { + name: "V", + requiredStage: PROGRESS_STAGE.V, + description: `V is the fourth Celestial, with a modified Reality which is similar to Teresa's Reality but only gives + rewards by reaching certain milestones of resources within. They give a new resource called Space Theorems, which + allow you to purchase additional Time Studies without path restrictions.` + }, + { + name: "Ra", + requiredStage: PROGRESS_STAGE.RA, + description: `Ra is the fifth Celestial, with a modified Reality which produces a resource called Memory Chunks + based on your resource totals within. They focus highly on taking older upgrades and themes from the previous + four Celestials and improving upon them, as well as filling out some final gaps in automation and convenience.` + }, + { + name: "Memories", + requiredStage: PROGRESS_STAGE.RA, + description: `Ra has the previous four Celestials under their control, producing Memories over time based on Memory + Chunk count. These Memories are used to level up the previous Celestials, providing upgrades when certain levels + are reached.` + }, + { + name: "Charged Infinity Upgrades", + requiredStage: PROGRESS_STAGE.RA, + description: `Teresa's Memories allow you to charge your Infinity Upgrades, maintaining a similar effect but + strengthening them significantly. Which upgrades are charged can only be changed between Realities.` + }, + { + name: "Glyph Alchemy", + requiredStage: PROGRESS_STAGE.RA, + description: `Effarig's Memories unlock Glyph Alchemy, which gives many minor boosts using a modified version of + Glyph Sacrifice. The resources gained from giving up Glyphs in this way must be combined together in reactions + in order to fully upgrade their effects.` + }, + { + name: "Amplified Black Hole", + requiredStage: PROGRESS_STAGE.RA, + description: `Nameless's Memories amplify charging so that the amount of game time stored is larger than the actual + game time elapsed. Discharging can now also be done repeatedly and automatically.` + }, + { + name: "Harder V", + requiredStage: PROGRESS_STAGE.RA, + description: `V's Memories unlocks a modified version of V's original Reality with even harder goals and a new set + of Time Studies called Triad Studies.` + }, + { + name: "Imaginary Machines", + requiredStage: PROGRESS_STAGE.IMAGINARY_MACHINES, + description: () => `Imaginary Machines are a new resource unlocked when reaching ${format(DC.E1000)} Reality + Machines. They are produced passively up to a cap determined by how many Reality Machines you would have gotten + in your farthest Reality ever.` + }, + { + name: "Lai'tela", + requiredStage: PROGRESS_STAGE.LAITELA, + description: `Lai'tela is the sixth Celestial, whose Reality has a modified completion condition and gives a + scaling reward based on how quickly you can reach it. They unlock new features largely related to a resource + called Dark Matter.` + }, + { + name: "Continuum", + requiredStage: PROGRESS_STAGE.LAITELA, + description: `Continuum is a modified type of production which allows your Antimatter Dimensions to produce as if + they could purchase fractional amounts of upgrades, without actually purchasing them.` + }, + { + name: "Dark Matter Dimensions", + requiredStage: PROGRESS_STAGE.LAITELA, + description: `Dark Matter Dimensions are cascading production which operate on a tick-based system instead of + continuously. The lowest tier produces Dark Matter and all tiers produce Dark Energy.` + }, + { + name: "Dimension Reset Mechanics", + requiredStage: PROGRESS_STAGE.LAITELA, + description: `Dark Matter Dimensions can be reset in two ways. Annihilation resets all your Dimensions in exchange + for a permanent multiplier to all Dark Matter Dimensions. Ascension increases production but resets the interval + of a single Dimension.` + }, + { + name: "Singularities", + requiredStage: PROGRESS_STAGE.LAITELA, + description: `Dark Energy can be used to produce Singularities, which give boosts based on their total amount. + When producing Singularities, any extra Dark Energy above the condensing threshold is wasted.` + }, + { + name: "Pelle", + requiredStage: PROGRESS_STAGE.PELLE, + description: `Pelle is the seventh and final Celestial, who permanently Dooms your game, throwing you into a very + difficult modified Reality which you cannot escape. Completing this Doomed Reality will beat the game.` + }, + { + name: "Armageddon", + requiredStage: PROGRESS_STAGE.PELLE, + description: `Armageddon is a Pelle-specific reset which you can perform at any time. This resets your progress to + the beginning of the Doomed Reality, but gives Remnants which produce Reality Shards.` + }, + { + name: "Pelle Strikes and Rifts", + requiredStage: PROGRESS_STAGE.PELLE, + description: `Upon reaching certain progress milestones within Pelle, a Strike may occur which permanently applies + another nerf to the Doomed Reality. Accompanying every Strike is a Rift, which is a mechanic which lets you drain + a different resource in exchange for a boost. These are permanent and remain unlocked after Armageddon.` + }, +]; diff --git a/javascripts/core/secret-formula/celestials/alchemy.js b/javascripts/core/secret-formula/celestials/alchemy.js index 7ba950abc..241821764 100644 --- a/javascripts/core/secret-formula/celestials/alchemy.js +++ b/javascripts/core/secret-formula/celestials/alchemy.js @@ -1,443 +1,462 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; -GameDatabase.celestials.alchemy = { - resources: { - // T1 resources (Non-Effarig "base" resources) - [ALCHEMY_RESOURCE.POWER]: { - name: "Power", - symbol: "Ω", - isBaseResource: true, - effect: amount => 1 + amount / 200000, - tier: 1, - uiOrder: 1, - unlockedAt: 2, - description: "provides a multiplier to Antimatter Dimensions", - formatEffect: value => `Antimatter Dimension multipliers ${formatPow(value, 4, 4)}` - }, - [ALCHEMY_RESOURCE.INFINITY]: { - name: "Infinity", - symbol: "∞", - isBaseResource: true, - effect: amount => 1 + amount / 200000, - tier: 1, - uiOrder: 2, - unlockedAt: 3, - description: "provides a multiplier to Infinity Dimensions", - formatEffect: value => `Infinity Dimension multipliers ${formatPow(value, 4, 4)}` - }, - [ALCHEMY_RESOURCE.TIME]: { - name: "Time", - symbol: "Δ", - isBaseResource: true, - effect: amount => 1 + amount / 200000, - tier: 1, - uiOrder: 3, - unlockedAt: 4, - description: "provides a multiplier to Time Dimensions", - formatEffect: value => `Time Dimension multipliers ${formatPow(value, 4, 4)}` - }, - [ALCHEMY_RESOURCE.REPLICATION]: { - name: "Replication", - symbol: "Ξ", - isBaseResource: true, - effect: amount => Decimal.pow10(amount / 1000), - tier: 1, - uiOrder: 4, - unlockedAt: 5, - description: `increases Replication Speed`, - formatEffect: value => `Replication speed is increased by ${formatX(value, 2, 2)}` - }, - [ALCHEMY_RESOURCE.DILATION]: { - name: "Dilation", - symbol: "Ψ", - isBaseResource: true, - effect: amount => Decimal.pow10(amount / 2000), - tier: 1, - uiOrder: 5, - unlockedAt: 6, - description: "increases Dilated Time production", - formatEffect: value => `Dilated Time production is increased by ${formatX(value, 2, 2)}` - }, +GameDatabase.celestials.alchemy.resources = { + // T1 resources (Non-Effarig "base" resources) + "power": { + id: ALCHEMY_RESOURCE.POWER, + name: "Power", + symbol: "Ω", + isBaseResource: true, + effect: amount => 1 + amount / 200000, + tier: 1, + uiOrder: 1, + unlockedAt: 2, + description: "provides a power to Antimatter Dimensions", + formatEffect: value => `Antimatter Dimension multipliers ${formatPow(value, 4, 4)}` + }, + "infinity": { + id: ALCHEMY_RESOURCE.INFINITY, + name: "Infinity", + symbol: "∞", + isBaseResource: true, + effect: amount => 1 + amount / 200000, + tier: 1, + uiOrder: 2, + unlockedAt: 3, + description: "provides a power to Infinity Dimensions", + formatEffect: value => `Infinity Dimension multipliers ${formatPow(value, 4, 4)}` + }, + "time": { + id: ALCHEMY_RESOURCE.TIME, + name: "Time", + symbol: "Δ", + isBaseResource: true, + effect: amount => 1 + amount / 200000, + tier: 1, + uiOrder: 3, + unlockedAt: 4, + description: "provides a power to Time Dimensions", + formatEffect: value => `Time Dimension multipliers ${formatPow(value, 4, 4)}` + }, + "replication": { + id: ALCHEMY_RESOURCE.REPLICATION, + name: "Replication", + symbol: "Ξ", + isBaseResource: true, + effect: amount => Decimal.pow10(amount / 1000), + tier: 1, + uiOrder: 4, + unlockedAt: 5, + description: `increases Replication speed`, + formatEffect: value => `Replication speed is increased by ${formatX(value, 2, 2)}` + }, + "dilation": { + id: ALCHEMY_RESOURCE.DILATION, + name: "Dilation", + symbol: "Ψ", + isBaseResource: true, + effect: amount => Decimal.pow10(amount / 2000), + tier: 1, + uiOrder: 5, + unlockedAt: 6, + description: "increases Dilated Time production", + formatEffect: value => `Dilated Time production is increased by ${formatX(value, 2, 2)}` + }, - // T2 resources (combinations of pairs of T1 resources) - [ALCHEMY_RESOURCE.CARDINALITY]: { - name: "Cardinality", - symbol: "α", - isBaseResource: false, - effect: amount => 1 + 0.2 / (1 + amount / 20000), - tier: 2, - uiOrder: 3, - unlockedAt: 8, - get description() { return `reduces the slowdown per ${format(Number.MAX_VALUE, 2)} Replicanti`; }, - formatEffect: value => `Replicanti interval increases slower ${formatX(1.2, 1, 1)} ➜ ` + - `${formatX(value, 4, 4)} per ${format(Number.MAX_VALUE, 2)}`, - reagents: [ - { - resource: ALCHEMY_RESOURCE.TIME, - amount: 8 - }, - { - resource: ALCHEMY_RESOURCE.REPLICATION, - amount: 7 - } - ] - }, - [ALCHEMY_RESOURCE.ETERNITY]: { - name: "Eternity", - symbol: "τ", - isBaseResource: false, - effect: amount => 1 + amount / 15000, - tier: 2, - uiOrder: 2, - unlockedAt: 9, - description: "increases Eternity generation", - formatEffect: value => `Eternity generation ${formatPow(value, 4, 4)}`, - reagents: [ - { - resource: ALCHEMY_RESOURCE.TIME, - amount: 11 - }, - { - resource: ALCHEMY_RESOURCE.INFINITY, - amount: 4 - } - ] - }, - [ALCHEMY_RESOURCE.DIMENSIONALITY]: { - name: "Dimensionality", - symbol: "ρ", - isBaseResource: false, - effect: amount => Decimal.pow10(5 * amount), - tier: 2, - uiOrder: 1, - unlockedAt: 10, - description: "provides a multiplier to all dimensions", - formatEffect: value => `All Dimensions ${formatX(value)}`, - reagents: [ - { - resource: ALCHEMY_RESOURCE.POWER, - amount: 10 - }, - { - resource: ALCHEMY_RESOURCE.INFINITY, - amount: 5 - } - ] - }, - [ALCHEMY_RESOURCE.INFLATION]: { - name: "Inflation", - symbol: "λ", - isBaseResource: false, - effect: amount => Decimal.pow10(6e9 - 3e5 * amount), - tier: 2, - uiOrder: 5, - unlockedAt: 11, - description: "increases multiplier effect over a threshold", - formatEffect: value => `All Antimatter Dimension multipliers are ${formatPow(1.05, 2, 2)} - if they are above ${format(value)} `, - reagents: [ - { - resource: ALCHEMY_RESOURCE.POWER, - amount: 9 - }, - { - resource: ALCHEMY_RESOURCE.DILATION, - amount: 6 - } - ] - }, - [ALCHEMY_RESOURCE.ALTERNATION]: { - name: "Alternation", - symbol: "ω", - isBaseResource: false, - effect: amount => amount / 200000, - tier: 2, - uiOrder: 4, - unlockedAt: 12, - description: "increases the strength of Tachyon Galaxies based on Replicanti", - formatEffect: value => `Tachyon Galaxies are ${formatPercents(value, 2, 2)} stronger ` + - `per ${format(DC.E1E6)} Replicanti`, - reagents: [ - { - resource: ALCHEMY_RESOURCE.REPLICATION, - amount: 5 - }, - { - resource: ALCHEMY_RESOURCE.DILATION, - amount: 10 - } - ] - }, - - // T3 resources (Effarig and conbinations of T1/T2 with Effarig) - [ALCHEMY_RESOURCE.EFFARIG]: { - name: "Effarig", - symbol: "Ϙ", - isBaseResource: true, - effect: amount => Math.pow(10, amount / 2500), - tier: 2, - uiOrder: 3.5, - unlockedAt: 7, - description: "increases Relic Shard gain", - formatEffect: value => `Relic Shard gain is multiplied ${formatX(value, 2, 2)}` - }, - [ALCHEMY_RESOURCE.SYNERGISM]: { - name: "Synergism", - symbol: "π", - isBaseResource: false, - effect: amount => Math.clampMax(0.3 + Math.sqrt(amount / 15000), 1), - tier: 3, - uiOrder: 2, - unlockedAt: 13, - description: "increases the effectiveness of Alchemy Reactions", - formatEffect(value) { - const baseEffect = `Alchemy reaction efficiency ${formatPercents(0.3)} ➜ ${formatPercents(value, 2, 2)}`; - if (player.reality.glyphs.sac.reality === 0) { - return baseEffect; - } - const increasedYield = formatPercents(value * Effects.sum(GlyphSacrifice.reality), 2, 2); - return `${baseEffect} (${increasedYield} after Glyph Sacrifice)`; + // T2 resources (combinations of pairs of T1 resources) + "cardinality": { + id: ALCHEMY_RESOURCE.CARDINALITY, + name: "Cardinality", + symbol: "α", + isBaseResource: false, + effect: amount => 1 + 0.2 / (1 + amount / 20000), + tier: 2, + uiOrder: 3, + unlockedAt: 8, + description: "reduces Replicanti slowdown when above the cap", + formatEffect: value => `Replicanti interval increases slower ${formatX(1.2, 1, 1)} ➜ + ${formatX(value, 4, 4)} per ${format(Number.MAX_VALUE, 2)}`, + reagents: [ + { + resource: ALCHEMY_RESOURCE.TIME, + amount: 8 }, - reagents: [ - { - resource: ALCHEMY_RESOURCE.EFFARIG, - amount: 3 - }, - { - resource: ALCHEMY_RESOURCE.REPLICATION, - amount: 16 - }, - { - resource: ALCHEMY_RESOURCE.INFINITY, - amount: 14 - } - ] - }, - [ALCHEMY_RESOURCE.MOMENTUM]: { - name: "Momentum", - symbol: "μ", - isBaseResource: false, - effect: amount => 1 + amount / 125000, - tier: 3, - uiOrder: 3, - unlockedAt: 15, - description: "provides a multiplier to all dimensions based on real time since unlock", - formatEffect: value => `All Dimensions ${formatPow(Ra.momentumValue, 4, 4)}, increasing by - ${format(0.002 * Achievement(175).effectOrDefault(1), 3, 3)} - per real-time hour after the resource is unlocked, up to a maximum of ${formatPow(value, 4, 4)}`, - reagents: [ - { - resource: ALCHEMY_RESOURCE.EFFARIG, - amount: 11 - }, - { - resource: ALCHEMY_RESOURCE.POWER, - amount: 4 - }, - { - resource: ALCHEMY_RESOURCE.TIME, - amount: 20 - } - ] - }, - [ALCHEMY_RESOURCE.DECOHERENCE]: { - name: "Decoherence", - symbol: "ξ", - isBaseResource: false, - effect: amount => 0.10 * Math.sqrt(amount / 10000), - tier: 3, - uiOrder: 4, - unlockedAt: 14, - description: "causes refining to give all basic Alchemy Resources", - formatEffect: value => `Refined Glyphs also give ${formatPercents(value, 2)} of their value ` + - "to all other base resources", - reagents: [ - { - resource: ALCHEMY_RESOURCE.EFFARIG, - amount: 13 - }, - { - resource: ALCHEMY_RESOURCE.ALTERNATION, - amount: 8 - } - ] - }, + { + resource: ALCHEMY_RESOURCE.REPLICATION, + amount: 7 + } + ] + }, + "eternity": { + id: ALCHEMY_RESOURCE.ETERNITY, + name: "Eternity", + symbol: "τ", + isBaseResource: false, + effect: amount => 1 + amount / 15000, + tier: 2, + uiOrder: 2, + unlockedAt: 9, + description: "provides a power to Eternity generation", + formatEffect: value => `Eternity generation ${formatPow(value, 4, 4)}`, + reagents: [ + { + resource: ALCHEMY_RESOURCE.TIME, + amount: 11 + }, + { + resource: ALCHEMY_RESOURCE.INFINITY, + amount: 4 + } + ] + }, + "dimensionality": { + id: ALCHEMY_RESOURCE.DIMENSIONALITY, + name: "Dimensionality", + symbol: "ρ", + isBaseResource: false, + effect: amount => Decimal.pow10(5 * amount), + tier: 2, + uiOrder: 1, + unlockedAt: 10, + description: "provides a large multiplier to all Dimensions", + formatEffect: value => `All Dimensions ${formatX(value)}`, + reagents: [ + { + resource: ALCHEMY_RESOURCE.POWER, + amount: 10 + }, + { + resource: ALCHEMY_RESOURCE.INFINITY, + amount: 5 + } + ] + }, + "inflation": { + id: ALCHEMY_RESOURCE.INFLATION, + name: "Inflation", + symbol: "λ", + isBaseResource: false, + effect: amount => Decimal.pow10(6e9 - 3e5 * amount), + tier: 2, + uiOrder: 5, + unlockedAt: 11, + description: "provides an additional power for very large multipliers", + formatEffect: value => `All Antimatter Dimension multipliers are ${formatPow(1.05, 2, 2)} + if they are above ${format(value)} `, + reagents: [ + { + resource: ALCHEMY_RESOURCE.POWER, + amount: 9 + }, + { + resource: ALCHEMY_RESOURCE.DILATION, + amount: 6 + } + ] + }, + "alternation": { + id: ALCHEMY_RESOURCE.ALTERNATION, + name: "Alternation", + symbol: "ω", + isBaseResource: false, + effect: amount => amount / 200000, + tier: 2, + uiOrder: 4, + unlockedAt: 12, + description: "increases the strength of Tachyon Galaxies based on Replicanti", + formatEffect: value => `Tachyon Galaxies are ${formatPercents(value, 2, 2)} stronger + per ${format(DC.E1E6)} Replicanti`, + reagents: [ + { + resource: ALCHEMY_RESOURCE.REPLICATION, + amount: 5 + }, + { + resource: ALCHEMY_RESOURCE.DILATION, + amount: 10 + } + ] + }, - // T4 resources (resources which feed directly into the final resource) - [ALCHEMY_RESOURCE.EXPONENTIAL]: { - name: "Exponential", - symbol: "Γ", - isBaseResource: false, - effect: amount => 10 * Math.pow(amount / 10000, 2), - tier: 4, - uiOrder: 2, - unlockedAt: 18, - description: "multiplies Infinity Points by Replicanti", - formatEffect: value => `Infinity Points multiplied by Replicanti${formatPow(value, 2, 3)}`, - reagents: [ - { - resource: ALCHEMY_RESOURCE.INFLATION, - amount: 18 - }, - { - resource: ALCHEMY_RESOURCE.SYNERGISM, - amount: 3 - } - ] + // T3 resources (Effarig and conbinations of T1/T2 with Effarig) + "effarig": { + id: ALCHEMY_RESOURCE.EFFARIG, + name: "Effarig", + symbol: "Ϙ", + isBaseResource: true, + effect: amount => Math.pow(10, amount / 2500), + tier: 1, + uiOrder: 1.5, + unlockedAt: 7, + description: "increases Relic Shard gain", + formatEffect: value => `Relic Shard gain is multiplied ${formatX(value, 2, 2)}` + }, + "synergism": { + id: ALCHEMY_RESOURCE.SYNERGISM, + name: "Synergism", + symbol: "π", + isBaseResource: false, + effect: amount => { + const rawValue = 0.3 + 1.3 * Math.sqrt(amount / 25000); + return Achievement(175).isUnlocked ? rawValue : Math.min(rawValue, 1); }, - [ALCHEMY_RESOURCE.FORCE]: { - name: "Force", - symbol: "Φ", - isBaseResource: false, - effect: amount => 5 * amount, - tier: 4, - uiOrder: 2, - unlockedAt: 17, - description: "multiplies Antimatter Dimensions by Reality Machines", - formatEffect: value => `Multiply Antimatter Dimensions by Reality Machines${formatPow(value, 2, 2)}`, - reagents: [ - { - resource: ALCHEMY_RESOURCE.DIMENSIONALITY, - amount: 7 - }, - { - resource: ALCHEMY_RESOURCE.MOMENTUM, - amount: 8 - } - ] - }, - [ALCHEMY_RESOURCE.UNCOUNTABILITY]: { - name: "Uncountability", - symbol: "Θ", - isBaseResource: false, - effect: amount => Math.sqrt(amount), - tier: 4, - uiOrder: 3, - unlockedAt: 19, - description: "passively generates Realities and Perk Points", - formatEffect: value => `Generate ${format(value, 2, 2)} Realities and Perk Points per second`, - reagents: [ - { - resource: ALCHEMY_RESOURCE.INFINITY, - amount: 20 - }, - { - resource: ALCHEMY_RESOURCE.EFFARIG, - amount: 6 - }, - { - resource: ALCHEMY_RESOURCE.CARDINALITY, - amount: 16 - } - ] - }, - [ALCHEMY_RESOURCE.BOUNDLESS]: { - name: "Boundless", - symbol: "Π", - isBaseResource: false, - effect: amount => amount / 80000, - tier: 4, - uiOrder: 1, - unlockedAt: 20, - description: "makes Tesseracts stronger", - formatEffect: value => `Tesseracts are +${formatPercents(value, 2, 2)} stronger`, - reagents: [ - { - resource: ALCHEMY_RESOURCE.ETERNITY, - amount: 13 - }, - { - resource: ALCHEMY_RESOURCE.INFLATION, - amount: 18 - } - ] - }, - [ALCHEMY_RESOURCE.MULTIVERSAL]: { - name: "Multiversal", - symbol: "Σ", - isBaseResource: false, - effect: amount => 5 * Math.pow(amount / 10000, 2), - tier: 4, - uiOrder: 5, - unlockedAt: 16, - description: "makes each Reality simulate more Realities", - formatEffect: value => `Each Reality simulates ${format(value, 2, 3)} additional Realities, giving all - the same rewards as if it was amplified`, - reagents: [ - { - resource: ALCHEMY_RESOURCE.ALTERNATION, - amount: 16 - }, - { - resource: ALCHEMY_RESOURCE.DECOHERENCE, - amount: 3 - } - ] - }, - [ALCHEMY_RESOURCE.UNPREDICTABILITY]: { - name: "Unpredictability", - symbol: "Λ", - isBaseResource: false, - effect: amount => amount / (10000 + amount), - tier: 4, - uiOrder: 4, - unlockedAt: 21, - description: "makes each Alchemy Reaction have a chance to happen twice", - formatEffect: value => `Any alchemy reaction has a ${formatPercents(value, 2, 2)} - chance of triggering again`, - reagents: [ - { - resource: ALCHEMY_RESOURCE.EFFARIG, - amount: 15 - }, - { - resource: ALCHEMY_RESOURCE.DECOHERENCE, - amount: 3 - }, - { - resource: ALCHEMY_RESOURCE.SYNERGISM, - amount: 10 - } - ] + tier: 3, + uiOrder: 2, + unlockedAt: 13, + description: "increases the yield of Alchemy Reactions", + formatEffect(value) { + return `Alchemy Reaction efficiency ${formatPercents(0.3)} ➜ ${formatPercents(value, 2, 2)} + ${(!Achievement(175).isUnlocked && value >= 1) ? " (Capped)" : ""}`; }, + reagents: [ + { + resource: ALCHEMY_RESOURCE.EFFARIG, + amount: 3 + }, + { + resource: ALCHEMY_RESOURCE.REPLICATION, + amount: 16 + }, + { + resource: ALCHEMY_RESOURCE.INFINITY, + amount: 14 + } + ] + }, + "momentum": { + id: ALCHEMY_RESOURCE.MOMENTUM, + name: "Momentum", + symbol: "μ", + isBaseResource: false, + effect: amount => 1 + amount / 125000, + tier: 3, + uiOrder: 3, + unlockedAt: 15, + description: "provides a power to all Dimensions that permanently grows over time", + formatEffect: value => `All Dimensions ${formatPow(Ra.momentumValue, 4, 4)}, increasing by + ${format(0.002 * Achievement(175).effectOrDefault(1), 3, 3)} + per real-time hour after the resource is unlocked, up to a maximum of ${formatPow(value, 4, 4)}`, + reagents: [ + { + resource: ALCHEMY_RESOURCE.EFFARIG, + amount: 11 + }, + { + resource: ALCHEMY_RESOURCE.POWER, + amount: 4 + }, + { + resource: ALCHEMY_RESOURCE.TIME, + amount: 20 + } + ] + }, + "decoherence": { + id: ALCHEMY_RESOURCE.DECOHERENCE, + name: "Decoherence", + symbol: "ξ", + isBaseResource: false, + effect: amount => 0.15 * Math.sqrt(amount / 25000), + tier: 3, + uiOrder: 4, + unlockedAt: 14, + description: "gives all basic Alchemy Resources upon refinement", + formatEffect: value => `Refined Glyphs also give ${formatPercents(value, 2)} of their value ` + + "to all other base resources", + reagents: [ + { + resource: ALCHEMY_RESOURCE.EFFARIG, + amount: 13 + }, + { + resource: ALCHEMY_RESOURCE.ALTERNATION, + amount: 8 + } + ] + }, - // T5 (Reality) - [ALCHEMY_RESOURCE.REALITY]: { - name: "Reality", - symbol: "Ϟ", - isBaseResource: false, - effect: amount => Math.floor(amount), - tier: 5, - unlockedAt: 25, - description: "allows creation of Reality Glyphs", - formatEffect: value => `Consume all Reality resource to create a level ${formatInt(value)} Reality Glyph`, - reagents: [ - { - resource: ALCHEMY_RESOURCE.EXPONENTIAL, - amount: 1 - }, - { - resource: ALCHEMY_RESOURCE.FORCE, - amount: 1 - }, - { - resource: ALCHEMY_RESOURCE.UNCOUNTABILITY, - amount: 1 - }, - { - resource: ALCHEMY_RESOURCE.BOUNDLESS, - amount: 1 - }, - { - resource: ALCHEMY_RESOURCE.MULTIVERSAL, - amount: 1 - }, - { - resource: ALCHEMY_RESOURCE.UNPREDICTABILITY, - amount: 1 - } - ] - }, + // T4 resources (resources which feed directly into the final resource) + "exponential": { + id: ALCHEMY_RESOURCE.EXPONENTIAL, + name: "Exponential", + symbol: "Γ", + isBaseResource: false, + effect: amount => 10 * Math.pow(amount / 10000, 2), + tier: 4, + uiOrder: 2, + unlockedAt: 18, + description: "multiplies Infinity Points based on Replicanti", + formatEffect: value => `Infinity Points multiplied by Replicanti${formatPow(value, 2, 3)}`, + reagents: [ + { + resource: ALCHEMY_RESOURCE.INFLATION, + amount: 18 + }, + { + resource: ALCHEMY_RESOURCE.SYNERGISM, + amount: 3 + } + ] + }, + "force": { + id: ALCHEMY_RESOURCE.FORCE, + name: "Force", + symbol: "Φ", + isBaseResource: false, + effect: amount => 5 * amount, + tier: 4, + uiOrder: 2, + unlockedAt: 17, + description: "multiplies Antimatter Dimensions based on Reality Machines", + formatEffect: value => `Multiply Antimatter Dimensions by Reality Machines${formatPow(value, 2, 2)}`, + reagents: [ + { + resource: ALCHEMY_RESOURCE.DIMENSIONALITY, + amount: 7 + }, + { + resource: ALCHEMY_RESOURCE.MOMENTUM, + amount: 8 + } + ] + }, + "uncountability": { + id: ALCHEMY_RESOURCE.UNCOUNTABILITY, + name: "Uncountability", + symbol: "Θ", + isBaseResource: false, + effect: amount => 160 * Math.sqrt(amount / 25000), + tier: 4, + uiOrder: 3, + unlockedAt: 19, + description: "passively generates Realities and Perk Points", + formatEffect: value => `Generate ${format(value, 2, 2)} Realities and Perk Points per second`, + reagents: [ + { + resource: ALCHEMY_RESOURCE.INFINITY, + amount: 20 + }, + { + resource: ALCHEMY_RESOURCE.EFFARIG, + amount: 6 + }, + { + resource: ALCHEMY_RESOURCE.CARDINALITY, + amount: 16 + } + ] + }, + "boundless": { + id: ALCHEMY_RESOURCE.BOUNDLESS, + name: "Boundless", + symbol: "Π", + isBaseResource: false, + effect: amount => amount / 80000, + tier: 4, + uiOrder: 1, + unlockedAt: 20, + description: "makes Tesseracts stronger", + formatEffect: value => `Tesseracts are +${formatPercents(value, 2, 2)} stronger`, + reagents: [ + { + resource: ALCHEMY_RESOURCE.ETERNITY, + amount: 13 + }, + { + resource: ALCHEMY_RESOURCE.INFLATION, + amount: 18 + } + ] + }, + "multiversal": { + id: ALCHEMY_RESOURCE.MULTIVERSAL, + name: "Multiversal", + symbol: "Σ", + isBaseResource: false, + effect: amount => 32 * Math.pow(amount / 25000, 2), + tier: 4, + uiOrder: 5, + unlockedAt: 16, + description: "makes each Reality simulate more Realities", + formatEffect: value => `Each Reality simulates ${format(value, 2, 3)} additional Realities, giving all + the same rewards as if it was amplified`, + reagents: [ + { + resource: ALCHEMY_RESOURCE.ALTERNATION, + amount: 16 + }, + { + resource: ALCHEMY_RESOURCE.DECOHERENCE, + amount: 3 + } + ] + }, + "unpredictability": { + id: ALCHEMY_RESOURCE.UNPREDICTABILITY, + name: "Unpredictability", + symbol: "Λ", + isBaseResource: false, + // Somewhat ugly number to make this show 70.00% at cap + effect: amount => amount / (10714.28 + amount), + tier: 4, + uiOrder: 4, + unlockedAt: 21, + description: "makes each Alchemy Reaction have a chance to happen twice", + formatEffect: value => `Any Alchemy Reaction has a ${formatPercents(value, 2, 2)} + chance of triggering again`, + reagents: [ + { + resource: ALCHEMY_RESOURCE.EFFARIG, + amount: 15 + }, + { + resource: ALCHEMY_RESOURCE.DECOHERENCE, + amount: 3 + }, + { + resource: ALCHEMY_RESOURCE.SYNERGISM, + amount: 10 + } + ] + }, + + // T5 (Reality) + "reality": { + id: ALCHEMY_RESOURCE.REALITY, + name: "Reality", + symbol: "Ϟ", + isBaseResource: false, + effect: amount => Math.floor(amount), + tier: 5, + unlockedAt: 25, + description: "can be consumed to create Reality Glyphs", + formatEffect: value => `Consume all Reality Resource to create a level ${formatInt(value)} Reality Glyph`, + reagents: [ + { + resource: ALCHEMY_RESOURCE.EXPONENTIAL, + amount: 1 + }, + { + resource: ALCHEMY_RESOURCE.FORCE, + amount: 1 + }, + { + resource: ALCHEMY_RESOURCE.UNCOUNTABILITY, + amount: 1 + }, + { + resource: ALCHEMY_RESOURCE.BOUNDLESS, + amount: 1 + }, + { + resource: ALCHEMY_RESOURCE.MULTIVERSAL, + amount: 1 + }, + { + resource: ALCHEMY_RESOURCE.UNPREDICTABILITY, + amount: 1 + } + ] }, }; diff --git a/javascripts/core/secret-formula/celestials/effarig.js b/javascripts/core/secret-formula/celestials/effarig.js index a44b35def..8df6d48a0 100644 --- a/javascripts/core/secret-formula/celestials/effarig.js +++ b/javascripts/core/secret-formula/celestials/effarig.js @@ -1,56 +1,67 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; -GameDatabase.celestials.effarig = { - unlocks: { - adjuster: { - id: 0, - description: "Adjustable Glyph level factor weights", - cost: 1e7 - }, - glyphFilter: { - id: 1, - description: "Glyph Filtering", - cost: 2e8 - }, - setSaves: { - id: 2, - description: "Glyph Set Saves", - cost: 3e9 - }, - run: { - id: 3, - description: "Effarig's Reality", - cost: 5e11 - }, - infinity: { - id: 4, - label: "Infinity", - get description() { - if (Pelle.isDoomed) return "Any rewards from Effarig's Infinity have been disabled."; - return ` Infinities raise the Replicanti cap +GameDatabase.celestials.effarig.unlocks = { + adjuster: { + id: 0, + description: "Adjustable Glyph level factor weights", + cost: 1e7, + onPurchased: () => { + Effarig.quotes.unlockWeights.show(); + ui.view.tabs.reality.openGlyphWeights = true; + Tab.reality.glyphs.show(); + } + }, + glyphFilter: { + id: 1, + description: "Glyph Filtering", + cost: 2e8, + onPurchased: () => { + Effarig.quotes.unlockGlyphFilter.show(); + player.reality.showSidebarPanel = GLYPH_SIDEBAR_MODE.FILTER_SETTINGS; + } + }, + setSaves: { + id: 2, + description: "Glyph Presets", + cost: 3e9, + onPurchased: () => { + Effarig.quotes.unlockSetSaves.show(); + player.reality.showSidebarPanel = GLYPH_SIDEBAR_MODE.SAVED_SETS; + } + }, + run: { + id: 3, + description: "Effarig's Reality", + cost: 5e11, + onPurchased: () => { + Effarig.quotes.unlockRun.show(); + } + }, + infinity: { + id: 4, + label: "Infinity", + get description() { + return ` Infinities raise the Replicanti cap Infinities increase your max Replicanti Galaxies Base Infinity Point gain is capped at ${format(DC.E200)} in Effarig's Reality Each type of Infinity Point multiplier is capped at ${format(DC.E50)} in Effarig's Reality`; - }, }, - eternity: { - id: 5, - label: "Eternity", - get description() { - if (Pelle.isDoomed) return "Any rewards from Effarig's Eternity have been disabled."; - return ` Eternities generates Infinities - Infinity Points are no longer limited in any way in Effarig's Reality - You have unlocked The Enslaved Ones`; - }, + }, + eternity: { + id: 5, + label: "Eternity", + get description() { + return ` Eternities generates Infinities + Infinity Points are no longer limited in any way in Effarig's Reality + You have unlocked The Nameless Ones`; + }, + }, + reality: { + id: 6, + label: "Reality", + get description() { + return " You have unlocked Effarig Glyphs (You may equip at most one and some effects are mutually exclusive)"; }, - reality: { - id: 6, - label: "Reality", - get description() { - if (Pelle.isDoomed) return "Any rewards from Effarig's Reality have been disabled."; - return " You have unlocked Effarig Glyphs (You may equip at most one)"; - }, - } } }; diff --git a/javascripts/core/secret-formula/celestials/enslaved.js b/javascripts/core/secret-formula/celestials/enslaved.js index 7da0c908a..730f4ff64 100644 --- a/javascripts/core/secret-formula/celestials/enslaved.js +++ b/javascripts/core/secret-formula/celestials/enslaved.js @@ -1,66 +1,59 @@ -import { GameDatabase } from "../game-database.js"; +import { GameDatabase } from "../game-database"; GameDatabase.celestials.enslaved = { - // Note that "condition" isn't displayed in-game. These are meant to be indicators here of - // precisely what the game is checking in order to reward the progress. // These entries will be unlocked in no particular order progress: { hintsUnlocked: { id: 0, - progress: "Unlocked Enslaved Hints", - hint: "The Enslaved Ones want to help, but the help takes a while.", - condition: `Spent more than 5 real-time hours inside the Reality without completing it; time outside the reality - counts for 4% as much. The timer accumulates and only starts once Enslaved's Reality is unlocked.`, + hint: "The Nameless Ones want to help, but the help takes a while.", + condition: () => `Spent more than ${formatInt(5)} real-time hours inside the Reality without completing it; + time outside the Reality counts for ${formatPercents(0.04)} as much. The timer starts once Nameless's + Reality is unlocked, but accumulates continuously.`, }, ec1: { id: 1, - progress: "\"Completed\" Eternity Challenge 1 more than five times at once", - hint: "That's odd, AutoEC seems to have some trouble working properly.", - condition: "Gained more than 5 completions of EC1 at once", + hint: "That's odd, the Automatic Eternity Challenge perk seems to be having some trouble working properly.", + condition: () => `Gained more than ${formatInt(5)} completions of Eternity Challenge 1 at once`, }, feelEternity: { id: 2, - progress: "Tried to Fix Infinity, but then experienced Eternity", hint: "Infinity seems to be more broken than usual in this Reality, but is that even fixable?", - condition: "Clicked the FEEL ETERNITY button", + condition: "Tried to Fix Infinity, but instead found and clicked the FEEL ETERNITY button", }, ec6: { id: 3, - progress: "Took advantage of cheaper Replicanti Galaxy scaling in an Eternity Challenge", - hint: "Some challenges are hard but also boost something in exchange, I wonder if there's a challenge " + - "that's just strictly better than normal here.", - condition: "Entered EC6 again after completing it 5 times", + hint: `Some Challenges are harder, but also boost something in exchange. I wonder if there's a Challenge + that's just strictly better than normal here.`, + condition: () => `Entered Eternity Challenge 6 again after completing it ${formatInt(5)} times in order + to use its cheaper Replicanti Galaxies`, }, c10: { id: 4, - progress: "Gained some Antimatter Galaxies using 6th Antimatter Dimensions", hint: "Is there a way to get Antimatter Galaxies without 8th Antimatter Dimensions?", - condition: "Used Challenge 10 to get more than one Antimatter Galaxy", + condition: "Used Challenge 10 to get more than one Antimatter Galaxy with 6th Antimatter Dimensions", }, secretStudy: { id: 5, - progress: "Found some extra secret Time Theorems", hint: "Time Study 12? What's that?", - condition: "Clicked the secret Time Study", + condition: () => `Clicked the secret Time Study and gained an extra ${formatInt(100)} Time Theorems`, }, storedTime: { id: 6, - progress: "Spent years waiting for the Reality to continue on", hint: "It seems like certain parts of this Reality erode away if you wait long enough.", condition: "Discharged to have more than a year of game time this Reality", }, challengeCombo: { id: 7, - progress: "\"Exploited\" an interaction between a Normal Challenge and an Eternity Challenge", - hint: "Could I possibly use one challenge to get around a restriction in another challenge?", - condition: "Entered C10 while already inside of EC6", + hint: "Could I possibly use one Challenge to get around a restriction in another Challenge?", + condition: "Entered Challenge 10 while already inside of Eternity Challenge 6", }, }, // These get unlocked sequentially glyphHints: [ "Infinity and Dilation Glyphs seem confined too tightly to be useful at all.", "Power and Time Glyphs are particularly strong here.", - "Effarig Glyphs are only useful with the right effects, but you can complete the Reality without one. " + - "A Replication Glyph is very helpful, but it's not strictly necessary or quite as strong " + - "as Power and Time."] + `Effarig Glyphs are only useful with the right effects, but you can complete the Reality without one. + A Replication Glyph is very helpful, but it's not strictly necessary or quite as strong + as Power and Time.` + ] }; diff --git a/javascripts/core/secret-formula/celestials/galaxy-generator.js b/javascripts/core/secret-formula/celestials/galaxy-generator.js index dda1d0f96..0cfbd57ac 100644 --- a/javascripts/core/secret-formula/celestials/galaxy-generator.js +++ b/javascripts/core/secret-formula/celestials/galaxy-generator.js @@ -1,66 +1,65 @@ -import { GameDatabase } from "../game-database.js"; +import { GameDatabase } from "../game-database"; -GameDatabase.celestials.pelle.galaxyGeneratorUpgrades = (function() { - const formatCost = c => format(c, 2); +const formatCost = c => format(c, 2); - const rebuyable = config => { - const { id, description, cost, effect, formatEffect, currency, currencyLabel } = config; - return { - id, - description, - cost: () => cost(player.celestials.pelle.rebuyables[id]), - formatCost, - effect: (x = player.celestials.pelle.rebuyables[id]) => effect(x), - formatEffect, - currency, - currencyLabel - }; - }; +const rebuyable = config => { + const { id, description, cost, effect, formatEffect, currency, currencyLabel } = config; return { - additive: rebuyable({ - id: "galaxyGeneratorAdditive", - description: "Increase base Galaxy generation by 2", - cost: x => Math.pow(3, x), - effect: x => x * 2, - formatEffect: x => `${format(x, 2, 2)}/s`, - currency: () => Currency.galaxyGeneratorGalaxies, - currencyLabel: "Galaxy" - }), - multiplicative: rebuyable({ - id: "galaxyGeneratorMultiplicative", - description: "Multiply Galaxy generation", - cost: x => Math.pow(10, x), - effect: x => Decimal.pow(2.5, x), - formatEffect: x => formatX(x, 2, 1), - currency: () => Currency.galaxyGeneratorGalaxies, - currencyLabel: "Galaxy" - }), - antimatterMult: rebuyable({ - id: "galaxyGeneratorAntimatterMult", - description: "Multiply Galaxy generation", - cost: x => Decimal.pow("1e100000000", 10 ** x), - effect: x => Decimal.pow(2, x), - formatEffect: x => formatX(x, 2), - currency: () => Currency.antimatter, - currencyLabel: "Antimatter" - }), - IPMult: rebuyable({ - id: "galaxyGeneratorIPMult", - description: "Multiply Galaxy generation", - cost: x => Decimal.pow("1e2000000", 100 ** x), - effect: x => Decimal.pow(2, x), - formatEffect: x => formatX(x, 2), - currency: () => Currency.infinityPoints, - currencyLabel: "Infinity Point" - }), - EPMult: rebuyable({ - id: "galaxyGeneratorEPMult", - description: "Multiply Galaxy generation", - cost: x => Decimal.pow("1e10000", 1000 ** x), - effect: x => Decimal.pow(2, x), - formatEffect: x => formatX(x, 2), - currency: () => Currency.eternityPoints, - currencyLabel: "Eternity Point" - }), + id, + description, + cost: () => cost(player.celestials.pelle.rebuyables[id]), + formatCost, + effect: (x = player.celestials.pelle.rebuyables[id]) => effect(x), + formatEffect, + currency, + currencyLabel }; -}()); +}; + +GameDatabase.celestials.pelle.galaxyGeneratorUpgrades = { + additive: rebuyable({ + id: "galaxyGeneratorAdditive", + description: "Increase base Galaxy generation by 2", + cost: x => Math.pow(3, x), + effect: x => x * 2, + formatEffect: x => `${format(x, 2, 2)}/s`, + currency: () => Currency.galaxyGeneratorGalaxies, + currencyLabel: "Galaxy" + }), + multiplicative: rebuyable({ + id: "galaxyGeneratorMultiplicative", + description: "Multiply Galaxy generation", + cost: x => Math.pow(10, x), + effect: x => Decimal.pow(2.5, x), + formatEffect: x => formatX(x, 2, 1), + currency: () => Currency.galaxyGeneratorGalaxies, + currencyLabel: "Galaxy" + }), + antimatterMult: rebuyable({ + id: "galaxyGeneratorAntimatterMult", + description: "Multiply Galaxy generation", + cost: x => Decimal.pow("1e100000000", 10 ** x), + effect: x => Decimal.pow(2, x), + formatEffect: x => formatX(x, 2), + currency: () => Currency.antimatter, + currencyLabel: "Antimatter" + }), + IPMult: rebuyable({ + id: "galaxyGeneratorIPMult", + description: "Multiply Galaxy generation", + cost: x => Decimal.pow("1e2000000", 100 ** x), + effect: x => Decimal.pow(2, x), + formatEffect: x => formatX(x, 2), + currency: () => Currency.infinityPoints, + currencyLabel: "Infinity Point" + }), + EPMult: rebuyable({ + id: "galaxyGeneratorEPMult", + description: "Multiply Galaxy generation", + cost: x => Decimal.pow("1e10000", 1000 ** x), + effect: x => Decimal.pow(2, x), + formatEffect: x => formatX(x, 2), + currency: () => Currency.eternityPoints, + currencyLabel: "Eternity Point" + }), +}; diff --git a/javascripts/core/secret-formula/celestials/navigation-sigils/final-sigil.js b/javascripts/core/secret-formula/celestials/navigation-sigils/final-sigil.js new file mode 100644 index 000000000..5f690b17b --- /dev/null +++ b/javascripts/core/secret-formula/celestials/navigation-sigils/final-sigil.js @@ -0,0 +1,206 @@ +import { GameDatabase } from "../../game-database"; + +import { CELESTIAL_NAV_DRAW_ORDER } from "../navigation"; + +function sigilProgress() { + const riftProgress = PelleRifts.all.map(r => Math.clamp(r.realPercentage, 0, 1)).min(); + const generatorProgress = Math.log10(1 + GalaxyGenerator.generatedGalaxies) / 11; + return Math.clampMax(0.2 * riftProgress + 0.8 * generatorProgress, 1); +} + +// Determines styling, overall visibility, and placement/scaling of the sigil. Center and size are defined such that +// keeping the sigil within internal coordinates of ±1 will keep the sigil within a ±size box of the center coordinates +const SigilAttributes = { + visible: () => PelleRifts.all.map(r => Math.clamp(r.realPercentage, 0, 1)).min() > 0, + center: new Vector(400, 300), + size: 400, + color: "#00ffff", + canvasLayer: CELESTIAL_NAV_DRAW_ORDER.NODE_BG - 500, +}; + +function scaledPos(x, y) { + const att = SigilAttributes; + return new Vector(att.center.x + att.size * x, att.center.y + att.size * y); +} + +// Reflects a vector across the vertical line down the center of the sigil bounding box. Used to take advantage of the +// sigil having vertical symmetry, allowing us to cut down on hardcoded specifications by half +function reflectAcrossVertical(vec) { + return new Vector(2 * SigilAttributes.center.x - vec.x, vec.y); +} + +/** + * Method to make an appropriately-formatted entry to be fed into the navigation code + * + * @member {String} type String specifying the actual shape of the element to be drawn; must be "line" or "circle" + * @member {Object} att Object whose props list out attributes of the shape to be drawn: + * Line : start and end, specifying the two endpoints to draw between. + * Circle: center and radius to specify the circle path, initAngle and finalAngle to specify an arc segment to draw. + * 0 is rightward and positive angles draw clockwise. Note that making finalAngle-initialAngle a multiple of 2pi + * will cause curve decomposition errors in the curve-drawing code. + * @member {Object} fill Object specifying a segment for the fill as a fraction of the total sigil filling progress. + * Each particular segment will fill from 0% to 100% within the range init to init+weight, without over/underfilling. + * @member {String} colorOverride Color to use for rendering the element, used instead of SigilAttributes.color + */ +// eslint-disable-next-line max-params +function sigilShape(type, att, fill, colorOverride) { + let pos, path, pathStart, pathEnd; + switch (type) { + case "edge": + pos = att.start; + path = new LinearPath(att.start, att.end); + pathStart = 0; + pathEnd = 1; + break; + case "circle": + pos = att.center; + path = LogarithmicSpiral.fromPolarEndpoints(att.center, 0, att.radius * SigilAttributes.size, + 1, att.radius * SigilAttributes.size); + pathStart = att.initAngle; + pathEnd = att.finalAngle; + break; + default: + throw Error("Unrecognized shape in sigil specification"); + } + + return { + visible: () => SigilAttributes.visible() && sigilProgress() >= fill.init, + complete: () => Math.clamp((sigilProgress() - fill.init) / fill.weight, 0, 1), + // Note that att and fill aren't used in navigation rendering, but including them here massively simplifies the + // sigil reflection logic + att, + fill, + node: { + position: pos, + ring: { + rMajor: 0, + }, + }, + connector: { + pathStart, + pathEnd, + drawOrder: SigilAttributes.canvasLayer, + path, + fill: colorOverride ?? SigilAttributes.color, + completeWidth: SigilAttributes.size / 20, + noBG: true, + }, + }; +} + +// These coordinates should generally be kept within ±1; if the sigil needs to be larger then that should be changed in +// the SigilAttribute object instead. Naming conventions for this sigil use smaller numbers to denote nodes generally +// closer to the center line (whether by distance or path length), whereas abbreviated words are used to describe +// vertical position. C denotes horizontal center points. +const Positions = Object.freeze({ + circTop: scaledPos(0.333, -0.41), + circMid: scaledPos(0.5, 0.22), + circBot: scaledPos(0, 0.43), + topC: scaledPos(0, -0.4), + top1: scaledPos(0.08, -0.27), + top2: scaledPos(0.18, -0.35), + top3: scaledPos(0.28, -0.35), + mid: scaledPos(0.08, 0.05), + arm1: scaledPos(0.5, 0.05), + arm2: scaledPos(0.5, -0.15), + arm3: scaledPos(0.4, -0.15), + lowC: scaledPos(0, 0.22), + low1: scaledPos(0.42, 0.22), + botC: scaledPos(0, 0.31), + bot1: scaledPos(0.12, 0.43), + bot2: scaledPos(0.28, 0.43), +}); + +// List of specified primitive graphics elements with which to construct the sigil; see docstring of sigilShape +// for description of proper attribute specifications +const Shapes = { + botCircR: sigilShape("circle", + { center: Positions.circBot, radius: 0.12, initAngle: 0.5 * Math.PI, finalAngle: -0.5 * Math.PI }, + { init: 0, weight: 0.2 }), + botH: sigilShape("edge", + { start: Positions.bot1, end: Positions.bot2 }, + { init: 0.1, weight: 0.1 }), + lowH: sigilShape("edge", + { start: Positions.lowC, end: Positions.low1 }, + { init: 0.3, weight: 0.3 }), + circUp: sigilShape("circle", + { center: Positions.circMid, radius: 0.08, initAngle: Math.PI, finalAngle: 0 }, + { init: 0.6, weight: 0.1 }), + circDown: sigilShape("circle", + { center: Positions.circMid, radius: 0.08, initAngle: Math.PI, finalAngle: 2 * Math.PI }, + { init: 0.6, weight: 0.1 }), + vert2: sigilShape("edge", + { start: Positions.bot2, end: Positions.top3 }, + { init: 0.2, weight: 0.7 }), + vertC: sigilShape("edge", + { start: Positions.botC, end: Positions.lowC }, + { init: 0.2, weight: 0.1 }), + vertDiag1: sigilShape("edge", + { start: Positions.lowC, end: Positions.mid }, + { init: 0.3, weight: 0.1 }), + arm1: sigilShape("edge", + { start: Positions.mid, end: Positions.arm1 }, + { init: 0.4, weight: 0.2 }), + arm2: sigilShape("edge", + { start: Positions.arm1, end: Positions.arm2 }, + { init: 0.6, weight: 0.1 }), + arm3: sigilShape("edge", + { start: Positions.arm2, end: Positions.arm3 }, + { init: 0.7, weight: 0.1 }), + vert1: sigilShape("edge", + { start: Positions.mid, end: Positions.top1 }, + { init: 0.4, weight: 0.3 }), + vertDiag2: sigilShape("edge", + { start: Positions.top1, end: Positions.topC }, + { init: 0.7, weight: 0.1 }), + vertDiag3: sigilShape("edge", + { start: Positions.top1, end: Positions.top2 }, + { init: 0.7, weight: 0.1 }), + topH: sigilShape("edge", + { start: Positions.top2, end: Positions.top3 }, + { init: 0.8, weight: 0.1 }), + circTopUp: sigilShape("circle", + { center: Positions.circTop, radius: 0.08, initAngle: 0.75 * Math.PI, finalAngle: 1.75 * Math.PI }, + { init: 0.9, weight: 0.1 }), + circTopDown: sigilShape("circle", + { center: Positions.circTop, radius: 0.08, initAngle: 0.75 * Math.PI, finalAngle: -0.25 * Math.PI }, + { init: 0.9, weight: 0.1 }), +}; + +// The hardcoded elements in Shapes above only specify roughly half of the sigil; here we take all the existing entries +// and reflect them across the center line. Note that this technically duplicates one of the elements on top of itself +for (const key of Object.keys(Shapes)) { + const toReflect = Shapes[key]; + if (toReflect.connector.path instanceof LinearPath) { + Shapes[`${key}Ref`] = sigilShape("edge", + { start: reflectAcrossVertical(toReflect.att.start), end: reflectAcrossVertical(toReflect.att.end) }, + toReflect.fill); + } else if (toReflect.connector.path instanceof LogarithmicSpiral) { + Shapes[`${key}Ref`] = sigilShape("circle", + { center: reflectAcrossVertical(toReflect.att.center), radius: toReflect.att.radius, + initAngle: Math.PI - toReflect.att.initAngle, finalAngle: Math.PI - toReflect.att.finalAngle }, + toReflect.fill); + } +} + +// This segment adds multiple circular arcs around the entire sigil, which all fill simultaneously +const arcSegments = 16; +for (let arcIndex = 0; arcIndex < arcSegments; arcIndex++) { + const len = 2 * Math.PI / arcSegments; + const init = arcIndex * len; + Shapes[`arcInner${arcIndex}`] = sigilShape("circle", + { center: SigilAttributes.center, radius: 0.75, + initAngle: init, finalAngle: init + len }, + { init: 0.1, weight: 0.4 }, + "crimson"); + Shapes[`arcOuter${arcIndex}`] = sigilShape("circle", + { center: SigilAttributes.center, radius: 0.95, + initAngle: init, finalAngle: init - len }, + { init: 0.5, weight: 0.4 }, + "crimson"); +} + +GameDatabase.celestials.navSigils = { + ...GameDatabase.celestials.navSigils, + ...Object.values(Shapes).mapToObject((key, idx) => `final-sigil-${idx}`, val => val) +}; diff --git a/javascripts/core/secret-formula/celestials/navigation-sigils/galaxy-icon.js b/javascripts/core/secret-formula/celestials/navigation-sigils/galaxy-icon.js new file mode 100644 index 000000000..bc2abf75c --- /dev/null +++ b/javascripts/core/secret-formula/celestials/navigation-sigils/galaxy-icon.js @@ -0,0 +1,98 @@ +import { GameDatabase } from "../../game-database"; + +import { CELESTIAL_NAV_DRAW_ORDER, pelleStarPosition } from "../navigation"; + +// Determines styling, overall visibility, and placement/scaling of the sigil. Center and size are defined such that +// the sigil will largely stay within a circle of radius "size" centered on "center" +const SigilAttributes = { + visible: () => Pelle.hasGalaxyGenerator, + center: pelleStarPosition(0, 0), + size: 20, + color: "#00ffff", + canvasLayer: CELESTIAL_NAV_DRAW_ORDER.CANVAS_OVERLAY, +}; + +/** + * Method to make an appropriately-formatted entry to be fed into the navigation code + * + * @member {String} type String specifying the actual shape of the element to be drawn; must be "circle" or "arc" + * @member {Object} att Object whose props list out attributes of the shape to be drawn: + * center - Center of the circle or logarithmic spiral ("arc") being drawn + * radius - Only used for "circle" and is the radius of the circle being drawn + * initRadius/finalRadius - Radius endpoints for a spiral + * initAngle/finalAngle - Angular endpoints for a spiral + * @member {Object} draw Object whose props (thickness, layer) determine the thickness and z-index of this element + * @member {String} colorOverride Color to use for rendering the element, used instead of SigilAttributes.color + */ +// eslint-disable-next-line max-params +function sigilShape(type, att, draw, colorOverride) { + let pos, path, pathStart, pathEnd; + switch (type) { + case "circle": + pos = att.center; + path = LogarithmicSpiral.fromPolarEndpoints(att.center, 0, att.radius * SigilAttributes.size, + 1, att.radius * SigilAttributes.size); + pathStart = att.initAngle; + pathEnd = att.finalAngle; + break; + case "arc": + pos = att.center; + pathStart = att.initAngle; + pathEnd = att.finalAngle; + path = LogarithmicSpiral.fromPolarEndpoints(att.center, pathStart, att.initRadius * SigilAttributes.size, + pathEnd, att.finalRadius * SigilAttributes.size); + break; + default: + throw Error("Unrecognized shape in sigil specification"); + } + + return { + visible: () => SigilAttributes.visible(), + complete: () => 1, + node: { + position: pos, + ring: { + rMajor: SigilAttributes.size * draw.thickness, + }, + bgDrawOrder: SigilAttributes.canvasLayer + draw.layer, + }, + connector: { + pathStart, + pathEnd, + drawOrder: SigilAttributes.canvasLayer + draw.layer, + path, + fill: colorOverride ?? SigilAttributes.color, + completeWidth: SigilAttributes.size * draw.thickness, + noBG: true + }, + }; +} + +// List of specified primitive graphics elements with which to construct the sigil; see docstring of sigilShape +// for description of proper attribute specifications. These are two circular rings in the center of the galaxy +const Shapes = { + disc: sigilShape("circle", + { center: SigilAttributes.center, radius: 0.2, initAngle: 0, finalAngle: 6.28 }, + { thickness: 0.15, layer: 1 }, + "#88ffff"), + glow: sigilShape("circle", + { center: SigilAttributes.center, radius: 0.125, initAngle: 0, finalAngle: 6.28 }, + { thickness: 0.05, layer: 2 }, + "white"), +}; + +// This segment adds multiple circular arcs around the entire sigil, in a shape resembling a spiral galaxy +const arcSegments = 10; +for (let arcIndex = 0; arcIndex < arcSegments; arcIndex++) { + const len = 2 * Math.PI / arcSegments; + const init = arcIndex * len; + Shapes[`spiral${arcIndex}`] = sigilShape("arc", + { center: SigilAttributes.center, initRadius: 0.2, finalRadius: 0.9, initAngle: init, finalAngle: init + Math.PI }, + { thickness: 0.1, layer: 0 }, + "cyan"); +} + +GameDatabase.celestials.navSigils = { + ...GameDatabase.celestials.navSigils, + ...Object.values(Shapes).mapToObject((key, idx) => `galaxy-icon-${idx}`, val => val) +}; diff --git a/javascripts/core/secret-formula/celestials/navigation.js b/javascripts/core/secret-formula/celestials/navigation.js index 1e67eef05..f5674c9ad 100644 --- a/javascripts/core/secret-formula/celestials/navigation.js +++ b/javascripts/core/secret-formula/celestials/navigation.js @@ -1,12 +1,13 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; +import wordShift from "../../wordShift"; export function emphasizeEnd(fraction) { return Math.pow(fraction, 10); } export function vUnlockProgress(index) { - if (V.has(V_UNLOCKS.V_ACHIEVEMENT_UNLOCK)) return 1; + if (VUnlocks.vAchievementUnlock.isUnlocked) return 1; const db = Object.values(GameDatabase.celestials.v.mainUnlock).find(e => e.id === index); return db.progress(); } @@ -20,6 +21,58 @@ export function vUnlockLegendLabel(complete, index) { ]; } +// Angle is defined/rescaled so that 0 is the first rift, 4 is the last one, and all 5 are equally spaced around +// a circle. Starts at top-left and goes clockwise, reference point is that 3 is directly down. It's allowed to be +// non-integer since it's also used for off-center curve control points +export function pelleStarPosition(angle, scale) { + const pelleCenter = new Vector(750, 550); + const theta = (0.7 - 0.4 * angle) * Math.PI; + return new Vector(scale * Math.cos(theta), -scale * Math.sin(theta)).plus(pelleCenter); +} + +// Makes curved spokes connecting the center of Pelle to all the outer nodes corresponding to rifts +function pelleStarConnector(index, fillColor, isOverfill) { + return (function() { + // This should be half of the second argument used in pelleStarPosition when used to define rift node positions + const pelleSize = 75; + const pathStart = (0.4 * index + 0.5) * Math.PI; + + // Technically 2 should be about 1.929 and 4/3 should be about 1.328; exact values for both of these leave a small + // gap between the path and the node, so we round up a bit to make those go away + const pathEnd = pathStart + 2; + const path = LogarithmicSpiral.fromPolarEndpoints(pelleStarPosition(index + 0.5, pelleSize), + pathStart, pelleSize, pathEnd, 4 / 3 * pelleSize); + // The +0.01 prevents curve decomposition errors from happening + const pathPadStart = path.angleFromRadius(pelleSize + 0.01) - pathStart; + const pathPadEnd = pathEnd - path.angleFromRadius(4 / 3 * pelleSize); + return { + pathStart, + pathEnd, + path, + pathPadStart, + pathPadEnd, + fill: fillColor, + drawOrder: isOverfill ? CELESTIAL_NAV_DRAW_ORDER.NODE_OVERLAYS : undefined, + noBG: isOverfill, + }; + }()); +} + +const FILL_STATE = { + LOCKED: 0, + FILL: 1, + DRAIN: 2, + OVERFILL: 3 +}; + +function riftFillStage(name) { + const rift = PelleRifts[name.toLowerCase()]; + if (!rift.canBeApplied) return FILL_STATE.LOCKED; + if (!Pelle.hasGalaxyGenerator || rift.reducedTo === 1) return FILL_STATE.FILL; + if (rift.reducedTo < 1) return FILL_STATE.DRAIN; + return FILL_STATE.OVERFILL; +} + export const CELESTIAL_NAV_DRAW_ORDER = { // Node background is a black fuzzy circle drawn behind nodes. It can help show their // outline in some cases, and can be used in cases where a connector passes under a node @@ -27,1644 +80,1921 @@ export const CELESTIAL_NAV_DRAW_ORDER = { CONNECTORS: 1000, NODES: 2000, NODE_OVERLAYS: 3000, + CANVAS_OVERLAY: 4000, }; -GameDatabase.celestials.navigation = (function() { - const Positions = Object.freeze({ - teresa: new Vector(100, 100), - teresaPerkPointShop: new Vector(0, -50), +const Positions = Object.freeze({ + teresa: new Vector(100, 100), + teresaPerkPointShop: new Vector(0, -50), - effarigShop: new Vector(300, 0), - effarigRealityUnlock: new Vector(400, 50), - effarigNode: new Vector(550, 25), + effarigShop: new Vector(300, 0), + effarigRealityUnlock: new Vector(400, 50), + effarigNode: new Vector(550, 25), - enslavedReality: new Vector(650, 250), - enslavedGlyphLevel: new Vector(650 + 75 * Math.cos(Math.PI / 180 * -60), 250 + 75 * Math.sin(Math.PI / 180 * -60)), - enslavedGlyphRarity: new Vector(650 + 75 * Math.cos(Math.PI / 180 * 120), 250 + 75 * Math.sin(Math.PI / 180 * 120)), + enslavedReality: new Vector(650, 250), + enslavedGlyphLevel: new Vector(650 + 75 * Math.cos(Math.PI / 180 * -60), 250 + 75 * Math.sin(Math.PI / 180 * -60)), + enslavedGlyphRarity: new Vector(650 + 75 * Math.cos(Math.PI / 180 * 120), 250 + 75 * Math.sin(Math.PI / 180 * 120)), - vUnlockAchievement: new Vector(400, 350 + 50 * Math.sqrt(3)), - vAchievement0: new Vector(350, 350), - vAchievement1: new Vector(450, 350), - vAchievement2: new Vector(500, 350 + 50 * Math.sqrt(3)), - vAchievement3: new Vector(450, 350 + 100 * Math.sqrt(3)), - vAchievement4: new Vector(350, 350 + 100 * Math.sqrt(3)), - vAchievement5: new Vector(300, 350 + 50 * Math.sqrt(3)), + vUnlockAchievement: new Vector(400, 350 + 50 * Math.sqrt(3)), + vAchievement0: new Vector(350, 350), + vAchievement1: new Vector(450, 350), + vAchievement2: new Vector(500, 350 + 50 * Math.sqrt(3)), + vAchievement3: new Vector(450, 350 + 100 * Math.sqrt(3)), + vAchievement4: new Vector(350, 350 + 100 * Math.sqrt(3)), + vAchievement5: new Vector(300, 350 + 50 * Math.sqrt(3)), - raReality: new Vector(400, 200), - raPetTeresa: new Vector(400 + 85 * Math.sin(Math.PI / 180 * 252), 200 + 85 * Math.cos(Math.PI / 180 * 252)), - raPetEffarig: new Vector(400 + 85 * Math.sin(Math.PI / 180 * 140), 200 + 85 * Math.cos(Math.PI / 180 * 140)), - raPetEnslaved: new Vector(400 + 85 * Math.sin(Math.PI / 180 * 78), 200 + 85 * Math.cos(Math.PI / 180 * 78)), - raPetV: new Vector(400 + 85 * Math.sin(Math.PI / 180 * 0), 200 + 85 * Math.cos(Math.PI / 180 * 0)), + raReality: new Vector(400, 200), + raPetTeresa: new Vector(400 + 85 * Math.sin(Math.PI / 180 * 252), 200 + 85 * Math.cos(Math.PI / 180 * 252)), + raPetEffarig: new Vector(400 + 85 * Math.sin(Math.PI / 180 * 140), 200 + 85 * Math.cos(Math.PI / 180 * 140)), + raPetEnslaved: new Vector(400 + 85 * Math.sin(Math.PI / 180 * 78), 200 + 85 * Math.cos(Math.PI / 180 * 78)), + raPetV: new Vector(400 + 85 * Math.sin(Math.PI / 180 * 0), 200 + 85 * Math.cos(Math.PI / 180 * 0)), + + laitelaFirstCenter: new Vector(150, 450), + laitelaFirstLeft: new Vector(100, 500), + laitelaFirstRight: new Vector(200, 500), + laitelaSecondCenter: new Vector(150, 550), + laitelaSecondLeft: new Vector(100, 600), + laitelaSecondRight: new Vector(200, 600), + laitelaThirdCenter: new Vector(150, 650), + + pelleUnlock: new Vector(450, 580), + pelleAchievementRequirement: pelleStarPosition(0, 0), + pelleVacuum: pelleStarPosition(0, 150), + pelleDecay: pelleStarPosition(1, 150), + pelleChaos: pelleStarPosition(2, 150), + pelleRecursion: pelleStarPosition(3, 150), + pelleParadox: pelleStarPosition(4, 150), + + pelleGalaxyGen: pelleStarPosition(0, 0), +}); + +// Reduces boilerplate for rift line objects, but needs quite a few parameters to do so since there are three separate +// elements that render for filling - the initial fill, the drain, and then the overfill +// eslint-disable-next-line max-params +function pelleRiftFill(name, index, textAngle, fillType) { + let visibleCheck, progressFn, legendFn, percentFn, incompleteClass, nodeFill, connectorFill; + switch (fillType) { + case FILL_STATE.FILL: + // The curve starts inside of the node, so we give the completion variable a bit of a headstart so that we can + // immediately see some filling even when it's pretty much still empty + visibleCheck = () => riftFillStage(name) === FILL_STATE.FILL; + progressFn = () => Math.clamp(0.1 + PelleRifts[name.toLowerCase()].realPercentage / 0.9, 1e-6, 1); + legendFn = () => false; + percentFn = x => (x - 0.1) / 0.9; + incompleteClass = "c-celestial-nav__test-incomplete"; + nodeFill = "crimson"; + connectorFill = "crimson"; + break; + case FILL_STATE.DRAIN: + // The logarithmic curve code sometimes throws errors if you attempt to draw with complete === 0, so we cheat and + // make it a really tiny number that should format to 0 in most notations. We also do a pow in order to make it + // visually smoother, because the generator spiral blocks the bottom bit and makes it look static near the end of + // the drain + visibleCheck = () => riftFillStage(name) >= FILL_STATE.DRAIN; + progressFn = () => Math.clamp(Math.sqrt(PelleRifts[name.toLowerCase()].reducedTo), 1e-6, 1); + legendFn = () => riftFillStage(name) === FILL_STATE.DRAIN && PelleRifts[name.toLowerCase()].reducedTo < 1; + percentFn = x => x; + incompleteClass = "c-celestial-nav__drained-rift"; + nodeFill = "crimson"; + connectorFill = "#550919"; + break; + case FILL_STATE.OVERFILL: + visibleCheck = () => riftFillStage(name) === FILL_STATE.OVERFILL; + progressFn = () => Math.clamp(PelleRifts[name.toLowerCase()].percentage - 1, 1e-6, 1); + percentFn = x => x + 1; + legendFn = () => true; + incompleteClass = undefined; + nodeFill = "#ff7700"; + connectorFill = "#ff9900"; + break; + } - laitelaFirstCenter: new Vector(150, 450), - laitelaFirstLeft: new Vector(100, 500), - laitelaFirstRight: new Vector(200, 500), - laitelaSecondCenter: new Vector(150, 550), - laitelaSecondLeft: new Vector(100, 600), - laitelaSecondRight: new Vector(200, 600), - laitelaThirdCenter: new Vector(150, 650), - }); return { - "teresa-base": { - visible: () => true, - complete: () => 1, - node: { - clickAction: () => Tab.celestials.teresa.show(true), - completeClass: "c-celestial-nav__test-complete", - incompleteClass: "c-celestial-nav__test-incomplete", - position: Positions.teresa, - ring: { - rMajor: 78, - rMinor: 64, - }, - legend: { - text: "Teresa", - angle: 135, - diagonal: 32, - horizontal: 16, - }, + visible: () => Pelle.isDoomed && visibleCheck(), + complete: () => progressFn(), + node: { + clickAction: () => Tab.celestials.pelle.show(true), + incompleteClass, + position: Positions[`pelle${name}`], + fill: nodeFill, + ring: { + rMajor: 8, + }, + forceLegend: () => legendFn(), + legend: { + text: complete => [ + `${formatPercents(percentFn(complete), 1)} ${wordShift.wordCycle(PelleRifts[name.toLowerCase()].name)}` + ], + angle: textAngle, + diagonal: 30, + horizontal: 16, }, }, - "teresa-reality-unlock": { - visible: () => true, - complete: () => (Teresa.has(TERESA_UNLOCKS.RUN) - ? 1 : Decimal.pLog10(Teresa.pouredAmount) / Math.log10(TERESA_UNLOCKS.RUN.price)), - node: { - completeClass: "c-celestial-nav__test-complete", - incompleteClass: "c-celestial-nav__test-incomplete", - position: Positions.teresa, - ring: { - rMajor: 32, - rMinor: 22, - }, - legend: { - hideWhenCompleted: true, - text: () => { - const rm = Teresa.pouredAmount; - const cost = TERESA_UNLOCKS.RUN.price; - return `Pour ${format(rm, 2)} / ${format(cost, 2)} RM`; - }, - angle: 135, - diagonal: 16, - horizontal: 16, - }, - }, - connector: (function() { - const pathStart = -Math.PI; - const pathEnd = Math.PI; - const path = LogarithmicSpiral.fromPolarEndpoints(Positions.teresa, -Math.PI, 69, Math.PI, 26); - const pathPadStart = path.angleFromRadius(64 - 3) - pathStart; - const pathPadEnd = pathEnd - path.angleFromRadius(34); - return { - pathStart, - pathEnd, - path, - pathPadStart, - pathPadEnd, - }; - }()), - }, - "teresa-reality": { - visible: () => true, - complete: () => (Teresa.runCompleted ? 1 : 0), - node: { - clickAction: () => Tab.celestials.teresa.show(true), - completeClass: "c-celestial-nav__test-complete", - incompleteClass: "c-celestial-nav__test-incomplete", - symbol: "Ϟ", - position: Positions.teresa, - ring: { - rMajor: 16, - }, - alwaysShowLegend: true, - legend: { - text: "Teresa's Reality", - angle: -135, - diagonal: 96, - horizontal: 16, - }, - } - }, - "teresa-pp-shop": { - visible: () => true, - complete: () => (Teresa.has(TERESA_UNLOCKS.SHOP) - ? 1 : Decimal.pLog10(Teresa.pouredAmount) / Math.log10(TERESA_UNLOCKS.SHOP.price)), - node: { - clickAction: () => Tab.celestials.teresa.show(true), - completeClass: "c-celestial-nav__test-complete", - incompleteClass: "c-celestial-nav__test-incomplete", - position: Positions.teresaPerkPointShop, - ring: { - rMajor: 16, - rMinor: 0, - }, - legend: { - text: complete => { - if (complete >= 1) return "Perk Point Shop"; - const rm = Teresa.pouredAmount; - const cost = TERESA_UNLOCKS.SHOP.price; - return [ - "Perk Point Shop", - `Pour ${format(rm, 2)} / ${format(cost, 2)} Reality Machines` - ]; - }, - angle: -35, - diagonal: 16, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: LinearPath.connectCircles(Positions.teresa, 78 - 1, Positions.teresaPerkPointShop, 16 - 1), - completeWidth: 6, - incompleteWidth: 4, - } - }, - "effarig-shop": { - visible: () => true, - complete: () => (Teresa.has(TERESA_UNLOCKS.EFFARIG) - ? 1 : Decimal.pLog10(Teresa.pouredAmount) / Math.log10(TERESA_UNLOCKS.EFFARIG.price)), - node: { - clickAction: () => Tab.celestials.effarig.show(true), - completeClass: "c-celestial-nav__effarig", - incompleteClass: "c-celestial-nav__test-incomplete", - position: Positions.effarigShop, - ring: { - rMajor: 24, - }, - legend: { - text: complete => { - if (complete >= 1) return "Effarig's Shop"; - const rm = Teresa.pouredAmount; - const cost = TERESA_UNLOCKS.EFFARIG.price; - return [ - "Effarig", - `Pour ${format(rm, 2)} / ${format(cost, 2)} Reality Machines` - ]; - }, - angle: -135, - diagonal: 16, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: LinearPath.connectCircles(Positions.teresa, 78 - 1, Positions.effarigShop, 24 - 1), - fill: "url(#gradTeresaEffarig)", - } - }, - "effarig-reality-unlock": { - visible: () => Teresa.has(TERESA_UNLOCKS.EFFARIG), - // If the upgrade to unlock the reality isn't yet bought, clamp the progress at 99.9%, - // even if the player has enough relic shards to buy it. - complete: () => (EffarigUnlock.run.isUnlocked - ? 1 : Math.clampMax(0.999, Decimal.pLog10(Currency.relicShards.value) / - Math.log10(EffarigUnlock.run.cost))), - node: { - clickAction: () => Tab.celestials.effarig.show(true), - completeClass: "c-celestial-nav__effarig", - incompleteClass: "c-celestial-nav__test-incomplete", - position: Positions.effarigRealityUnlock, - ring: { - rMajor: 16, - }, - legend: { - text: complete => { - if (complete >= 1) return "Unlock Effarig's Reality"; - const rs = Currency.relicShards.value; - const cost = EffarigUnlock.run.cost; - return [ - "Unlock Effarig's Reality", - `Reach ${format(rs, 2)} / ${format(cost, 2)} Relic Shards` - ]; - }, - angle: 75, - diagonal: 40, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: LinearPath.connectCircles(Positions.effarigShop, 24 - 1, Positions.effarigRealityUnlock, 16 - 1), - fill: "#d13737", - } - }, - "effarig-infinity": { - visible: () => EffarigUnlock.run.isUnlocked, - complete: () => { - if (EffarigUnlock.infinity.isUnlocked) return 1; - if (!Effarig.isRunning) return 0; - - return Currency.antimatter.value.pLog10() / Decimal.NUMBER_MAX_VALUE.log10(); - }, - node: { - clickAction: () => Tab.celestials.effarig.show(true), - completeClass: "c-celestial-nav__effarig", - incompleteClass: "c-celestial-nav__test-incomplete", - position: Positions.effarigNode, - ring: { - rMajor: 60, - rMinor: 52, - }, - legend: { - text: complete => { - if (complete >= 1) return "Effarig's Infinity"; - if (complete === 0) return "Unlock Effarig's Reality"; - const am = Effarig.isRunning ? Currency.antimatter.value : 0; - return [ - "Effarig's Infinity", - `Reach ${format(am, 2)} / ${format(Number.MAX_VALUE, 2)}`, - "Antimatter inside Effarig's Reality." - ]; - }, - angle: 0, - diagonal: 100, - horizontal: 16, - }, - bgDrawOrder: CELESTIAL_NAV_DRAW_ORDER.NODE_BG + 750, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: LinearPath.connectCircles(Positions.effarigRealityUnlock, 16 - 1, Positions.effarigNode, 60 - 1), - fill: "#d13737", - } - }, - "effarig-eternity": { - visible: () => EffarigUnlock.infinity.isUnlocked, - complete: () => { - if (EffarigUnlock.eternity.isUnlocked) return 1; - if (!Effarig.isRunning) return 0; - - return Currency.infinityPoints.value.pLog10() / Decimal.NUMBER_MAX_VALUE.log10(); - }, - node: { - clickAction: () => Tab.celestials.effarig.show(true), - completeClass: "c-celestial-nav__effarig", - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#7131ec", - position: Positions.effarigNode, - ring: { - rMajor: 40, - rMinor: 30, - }, - legend: { - text: complete => { - if (complete >= 1) return "Effarig's Eternity"; - const ip = Effarig.isRunning ? Currency.infinityPoints.value : 0; - return [ - "Effarig's Eternity", - `Reach ${format(ip, 2)} / ${format(Number.MAX_VALUE, 2)}`, - "Infinity Points inside Effarig's Reality." - ]; - }, - angle: -45, - diagonal: 16, - horizontal: 16, - }, - }, - connector: (function() { - const pathStart = -Math.PI; - const pathEnd = 0; - const path = LogarithmicSpiral.fromPolarEndpoints(new Vector(560, 25), pathStart, 66, pathEnd, 26); - const pathPadStart = 0; - const pathPadEnd = pathEnd - path.angleFromRadius(30); - return { - pathStart, - pathEnd, - path, - pathPadStart, - pathPadEnd, - fill: "#d13737" - }; - }()) - }, - "effarig-reality": { - visible: () => EffarigUnlock.eternity.isUnlocked, - complete: () => { - if (EffarigUnlock.reality.isUnlocked) return 1; - if (!Effarig.isRunning) return 0; - - return Currency.eternityPoints.value.pLog10() / 4000; - }, - node: { - clickAction: () => Tab.celestials.effarig.show(true), - completeClass: "c-celestial-nav__effarig", - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#A101ec", - position: new Vector(550, 25), - ring: { - rMajor: 20, - rMinor: 0, - }, - symbol: "Ϙ", - alwaysShowLegend: true, - legend: { - text: complete => { - if (complete >= 1) return "Effarig's Reality"; - const ep = Effarig.isRunning ? Currency.eternityPoints.value : 0; - const goal = DC.E4000; - return [ - "Effarig's Reality", - `Reach ${format(ep, 2)} / ${format(goal, 2)}`, - "Eternity Points inside Effarig's Reality." - ]; - }, - angle: -120, - diagonal: 82, - horizontal: 16, - }, - }, - connector: (function() { - const pathStart = 0; - const pathEnd = Math.PI; - const path = LogarithmicSpiral.fromPolarEndpoints(new Vector(558, 25), pathStart, 26, pathEnd, 24); - const pathPadStart = 0; - const pathPadEnd = 0; - return { - pathStart, - pathEnd, - path, - pathPadStart, - pathPadEnd, - fill: "#d13737" - }; - }()) - }, - "enslaved": { - visible: () => EffarigUnlock.eternity.isUnlocked, - complete: () => (EffarigUnlock.eternity.isUnlocked ? 1 : 0), - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.enslaved.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffa337", - position: Positions.enslavedReality, - ring: { - rMajor: 80, - rMinor: 70, - gapCenterDeg: 15, - gapDeg: 200, - }, - alwaysShowLegend: false, - legend: { - text: "Enslaved", - angle: -90, - diagonal: 20, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - drawOrder: CELESTIAL_NAV_DRAW_ORDER.NODE_BG + 500, - path: LinearPath.connectCircles(Positions.effarigNode, 40 - 1, Positions.enslavedReality, 80 - 1), - fill: "url(#gradEffarigEnslaved)", - } - }, - "enslaved-unlock-glyph-level": { - visible: () => EffarigUnlock.eternity.isUnlocked, - complete: () => player.records.bestReality.glyphLevel / 5000, - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.enslaved.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffa337", - position: Positions.enslavedGlyphLevel, - ring: { - rMajor: 24, - rMinor: 16, - gapCenterDeg: 40, - gapDeg: 60, - gapAngleDeg: 0, - }, - legend: { - text: complete => { - if (complete >= 1) return "Broken the chain with Glyph level"; - const goal = 5000; - return [ - "Break a chain", - `Reach Glyph level ${formatInt(Math.min(player.records.bestReality.glyphLevel, goal))}/${formatInt(goal)}` - ]; - }, - angle: -45, - diagonal: 16, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: new LinearPath( - new Vector(650 - 74 * Math.sqrt(0.75), 250 - 74 * 0.5), - Positions.enslavedGlyphLevel) - .trimEnd(23), - fill: "#ffa337", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "enslaved-unlock-glyph-rarity": { - visible: () => EffarigUnlock.eternity.isUnlocked, - complete: () => { - const bestRarity = strengthToRarity(player.records.bestReality.glyphStrength); - return bestRarity / 100; - }, - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.enslaved.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffa337", - position: Positions.enslavedGlyphRarity, - ring: { - rMajor: 24, - rMinor: 16, - gapCenterDeg: 220, - gapDeg: 60, - gapAngleDeg: 0, - }, - legend: { - text: complete => { - if (complete >= 1) return "Broken the chain with Glyph rarity"; - const goal = 100; - return [ - "Break a chain", - `Reach Glyph rarity ${formatPercents(complete * goal / 100, 1)}/${formatPercents(goal / 100, 1)}` - ]; - }, - angle: 135, - diagonal: 32, - horizontal: 32, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: new LinearPath(Positions.enslavedGlyphRarity, Positions.enslavedGlyphLevel).trimStart(23).trimEnd(23), - fill: "#ffa337", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "enslaved-reality": { - visible: () => EffarigUnlock.eternity.isUnlocked, - complete: () => { - if (Enslaved.isCompleted) return 1; - if (!Enslaved.isRunning) return 0; - - return Currency.eternityPoints.value.pLog10() / 4000; - }, - node: { - clickAction: () => Tab.celestials.enslaved.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffa337", - position: Positions.enslavedReality, - ring: { - rMajor: 80, - rMinor: 70, - gapCenterDeg: 195, - gapDeg: 200, - }, - alwaysShowLegend: true, - legend: { - text: complete => { - if (complete >= 1) return "The Enslaved Ones' Reality"; - const ep = Enslaved.isRunning ? Currency.eternityPoints.value : 0; - const goal = DC.E4000; - return [ - "The Enslaved Ones' Reality", - `Reach ${format(ep, 2)} / ${format(goal, 2)}`, - "Eternity Points inside The Enslaved Ones' Reality." - ]; - }, - angle: 45, - diagonal: 16, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: new LinearPath(Positions.enslavedGlyphRarity, new Vector(650 + 74 * Math.sqrt(0.75), 250 + 74 * 0.5)) - .trimStart(23), - fill: "#ffa337", - } - }, - "v-unlock-achievement": { - visible: () => EffarigUnlock.reality.isUnlocked, - complete: () => { - if (Achievement(151).isUnlocked) return 1; - if (!player.requirementChecks.infinity.noAD8) return 0; - - return player.galaxies / 800; - }, - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.v.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - symbol: "⌬", - symbolOffset: "2", - fill: "#ffe066", - position: Positions.vUnlockAchievement, - ring: { - rMajor: 20, - }, - alwaysShowLegend: true, - legend: { - text: complete => { - const goal = 800; - if (complete >= 1) return "V's Reality"; - const galaxies = player.requirementChecks.infinity.noAD8 ? player.galaxies : 0; - return [ - "V's unlock Achievement", - `Reach ${formatInt(galaxies)} / ${formatInt(goal)} Antimatter Galaxies without buying`, - "8th Antimatter Dimensions in your current Infinity" - ]; - }, - angle: 35, - diagonal: 60, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: LinearPath.connectCircles(Positions.enslavedReality, 80 - 1, Positions.vUnlockAchievement, 16 - 1), - fill: "url(#gradEnslavedV)", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "v-unlock-1": { - visible: () => Achievement(151).isUnlocked || V.has(V_UNLOCKS.V_ACHIEVEMENT_UNLOCK), - complete: () => vUnlockProgress(1), - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.v.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffe066", - position: Positions.vAchievement1, - ring: { - rMajor: 8, - }, - legend: { - text: complete => vUnlockLegendLabel(complete, 1), - angle: -135, - diagonal: 16, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: new LinearPath(Positions.vUnlockAchievement, Positions.vAchievement1), - fill: "#ffe066", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "v-unlock-2": { - visible: () => Achievement(151).isUnlocked || V.has(V_UNLOCKS.V_ACHIEVEMENT_UNLOCK), - complete: () => vUnlockProgress(2), - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.v.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffe066", - position: Positions.vAchievement2, - ring: { - rMajor: 8, - }, - legend: { - text: complete => vUnlockLegendLabel(complete, 2), - angle: -135, - diagonal: 30, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: new LinearPath(Positions.vUnlockAchievement, Positions.vAchievement2), - fill: "#ffe066", - completeWidth: 6, - incompleteWidth: 4, - } - }, - - "v-unlock-3": { - visible: () => Achievement(151).isUnlocked || V.has(V_UNLOCKS.V_ACHIEVEMENT_UNLOCK), - complete: () => vUnlockProgress(3), - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.v.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffe066", - position: Positions.vAchievement3, - ring: { - rMajor: 8, - }, - legend: { - text: complete => vUnlockLegendLabel(complete, 3), - angle: -135, - diagonal: 45, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: new LinearPath(Positions.vUnlockAchievement, Positions.vAchievement3), - fill: "#ffe066", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "v-unlock-4": { - visible: () => Achievement(151).isUnlocked || V.has(V_UNLOCKS.V_ACHIEVEMENT_UNLOCK), - complete: () => vUnlockProgress(4), - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.v.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffe066", - position: Positions.vAchievement4, - ring: { - rMajor: 8, - }, - legend: { - text: complete => vUnlockLegendLabel(complete, 4), - angle: -135, - diagonal: 60, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: new LinearPath(Positions.vUnlockAchievement, Positions.vAchievement4), - fill: "#ffe066", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "v-unlock-5": { - visible: () => Achievement(151).isUnlocked || V.has(V_UNLOCKS.V_ACHIEVEMENT_UNLOCK), - complete: () => vUnlockProgress(5), - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.v.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffe066", - position: Positions.vAchievement5, - ring: { - rMajor: 8, - }, - legend: { - text: complete => vUnlockLegendLabel(complete, 5), - angle: -135, - diagonal: 75, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: new LinearPath(Positions.vUnlockAchievement, Positions.vAchievement5), - fill: "#ffe066", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "v-unlock-6": { - visible: () => Achievement(151).isUnlocked || V.has(V_UNLOCKS.V_ACHIEVEMENT_UNLOCK), - complete: () => vUnlockProgress(6), - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.v.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffe066", - position: Positions.vAchievement0, - ring: { - rMajor: 8, - }, - legend: { - text: complete => vUnlockLegendLabel(complete, 6), - angle: -135, - diagonal: 90, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: new LinearPath(Positions.vUnlockAchievement, Positions.vAchievement0), - fill: "#ffe066", - completeWidth: 6, - incompleteWidth: 4, - } - }, - - "v-achievement-0": { - visible: () => V.has(V_UNLOCKS.V_ACHIEVEMENT_UNLOCK), - complete: () => VRunUnlocks.all[0].completions / 6, - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.v.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffe066", - position: Positions.vAchievement0, - ring: { - rMajor: 8, - }, - legend: { - text: complete => { - const name = VRunUnlocks.all[0].config.name; - if (complete >= 1) return `V-Achievement "${name}"`; - const completions = VRunUnlocks.all[0].completions; - return [ - "V-Achievement", - `Reach ${formatInt(completions)} / ${formatInt(6)} completions in ${name}.` - ]; - }, - angle: -135, - diagonal: 16, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: new LinearPath(Positions.vAchievement5, Positions.vAchievement0), - fill: "#ffe066", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "v-achievement-1": { - visible: () => V.has(V_UNLOCKS.V_ACHIEVEMENT_UNLOCK), - complete: () => VRunUnlocks.all[1].completions / 6, - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.v.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffe066", - position: Positions.vAchievement1, - ring: { - rMajor: 8, - }, - legend: { - text: complete => { - const name = VRunUnlocks.all[1].config.name; - if (complete >= 1) return `V-Achievement "${name}"`; - const completions = VRunUnlocks.all[1].completions; - return [ - "V-Achievement", - `Reach ${formatInt(completions)} / ${formatInt(6)} completions in ${name}.` - ]; - }, - angle: 20, - diagonal: 16, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: new LinearPath(Positions.vAchievement0, Positions.vAchievement1), - fill: "#ffe066", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "v-achievement-2": { - visible: () => V.has(V_UNLOCKS.V_ACHIEVEMENT_UNLOCK), - complete: () => VRunUnlocks.all[2].completions / 6, - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.v.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffe066", - position: Positions.vAchievement2, - ring: { - rMajor: 8, - }, - legend: { - text: complete => { - const name = VRunUnlocks.all[2].config.name; - if (complete >= 1) return `V-Achievement "${name}"`; - const completions = VRunUnlocks.all[2].completions; - return [ - "V-Achievement", - `Reach ${formatInt(completions)} / ${formatInt(6)} completions in ${name}.` - ]; - }, - angle: 45, - diagonal: 16, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: new LinearPath(Positions.vAchievement1, Positions.vAchievement2), - fill: "#ffe066", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "v-achievement-3": { - visible: () => V.has(V_UNLOCKS.V_ACHIEVEMENT_UNLOCK), - complete: () => VRunUnlocks.all[3].completions / 6, - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.v.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffe066", - position: Positions.vAchievement3, - ring: { - rMajor: 8, - }, - legend: { - text: complete => { - const name = VRunUnlocks.all[3].config.name; - if (complete >= 1) return `V-Achievement "${name}"`; - const completions = VRunUnlocks.all[3].completions; - return [ - "V-Achievement", - `Reach ${formatInt(completions)} / ${formatInt(6)} completions in ${name}.` - ]; - }, - angle: 45, - diagonal: 16, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: new LinearPath(Positions.vAchievement2, Positions.vAchievement3), - fill: "#ffe066", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "v-achievement-4": { - visible: () => V.has(V_UNLOCKS.V_ACHIEVEMENT_UNLOCK), - complete: () => VRunUnlocks.all[4].completions / 6, - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.v.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffe066", - position: Positions.vAchievement4, - ring: { - rMajor: 8, - }, - legend: { - text: complete => { - const name = VRunUnlocks.all[4].config.name; - if (complete >= 1) return `V-Achievement "${name}"`; - const completions = VRunUnlocks.all[4].completions; - return [ - "V-Achievement", - `Reach ${formatInt(completions)} / ${formatInt(6)} completions in ${name}.` - ]; - }, - angle: 45, - diagonal: 16, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: new LinearPath(Positions.vAchievement3, Positions.vAchievement4), - fill: "#ffe066", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "v-achievement-5": { - visible: () => V.has(V_UNLOCKS.V_ACHIEVEMENT_UNLOCK), - complete: () => VRunUnlocks.all[5].completions / 6, - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.v.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#ffe066", - position: Positions.vAchievement5, - ring: { - rMajor: 8, - }, - legend: { - text: complete => { - const name = VRunUnlocks.all[5].config.name; - if (complete >= 1) return `V-Achievement "${name}"`; - const completions = VRunUnlocks.all[5].completions; - return [ - "V-Achievement", - `Reach ${formatInt(completions)} / ${formatInt(6)} completions in ${name}.` - ]; - }, - angle: 100, - diagonal: 16, - horizontal: 16, - }, - }, - connector: { - pathStart: 0, - pathEnd: 1, - path: new LinearPath(Positions.vAchievement4, Positions.vAchievement5), - fill: "#ffe066", - completeWidth: 6, - incompleteWidth: 4, - } - }, - - "ra": { - visible: () => V.has(V_UNLOCKS.RA_UNLOCK), - complete: () => (V.has(V_UNLOCKS.RA_UNLOCK) ? 1 : 0), - node: { - clickAction: () => Tab.celestials.ra.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - symbol: "\uf185", - symbolOffset: "2", - fill: "#9063de", - position: Positions.raReality, - ring: { - rMajor: 24, - }, - alwaysShowLegend: true, - legend: { - text: "Ra's Reality", - angle: 230, - diagonal: 85, - horizontal: 16, - }, - } - }, - "teresa-pet": { - visible: () => V.has(V_UNLOCKS.RA_UNLOCK), - complete: () => (V.has(V_UNLOCKS.RA_UNLOCK) ? 1 : 0), - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.ra.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#9063de", - isStacked: true, - position: Positions.raPetTeresa, - ring: { - rMajor: 12, - }, - legend: { - text: () => { - const level = Ra.pets.teresa.level; - if (level === 25) return `Ra's Teresa Memories have all been returned`; - return [ - "Ra's Teresa Memory level", - `${formatInt(level)} / ${formatInt(25)}` - ]; - }, - angle: 142, - diagonal: 85, - horizontal: 16, - }, - }, - connector: { - pathStart: 0.05, - pathEnd: 0.95, - path: new LinearPath(Positions.raReality, Positions.raPetTeresa), - fill: "#9063de", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "teresa-pet-to-teresa": { - visible: () => V.has(V_UNLOCKS.RA_UNLOCK), - complete: () => Ra.pets.teresa.level / 25, - drawOrder: -1, - connector: { - pathStart: 0.05, - pathEnd: 0.70, - path: new LinearPath(Positions.raPetTeresa, Positions.teresa), - fill: "url(#gradRaTeresa)", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "effarig-pet": { - visible: () => V.has(V_UNLOCKS.RA_UNLOCK), - complete: () => Ra.pets.teresa.level / 8, - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.ra.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#9063de", - isStacked: true, - position: Positions.raPetEffarig, - ring: { - rMajor: 12, - }, - legend: { - text: complete => { - const unlocked = Ra.pets.teresa.level; - const level = Ra.pets.effarig.level; - if (complete < 1) return `Ra's Teresa Memory level ${unlocked} / ${formatInt(8)}`; - if (level === 25) return `Ra's Effarig Memories have all been returned`; - return [ - "Ra's Effarig Memory level", - `${formatInt(level)} / ${formatInt(25)}` - ]; - }, - angle: 142, - diagonal: 85, - horizontal: 16, - }, - }, - connector: { - pathStart: 0.05, - pathEnd: 0.95, - path: new LinearPath(Positions.raReality, Positions.raPetEffarig), - fill: "#9063de", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "effarig-pet-to-effarig": { - visible: () => Ra.has(RA_UNLOCKS.EFFARIG_UNLOCK), - complete: () => Ra.pets.effarig.level / 25, - drawOrder: -1, - connector: { - pathStart: 0.05, - pathEnd: 0.60, - path: new LinearPath(Positions.raPetEffarig, Positions.effarigNode), - fill: "url(#gradRaEffarig)", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "enslaved-pet": { - visible: () => Ra.has(RA_UNLOCKS.EFFARIG_UNLOCK), - complete: () => Ra.pets.effarig.level / 8, - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.ra.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#9063de", - isStacked: true, - position: Positions.raPetEnslaved, - ring: { - rMajor: 12, - }, - legend: { - text: complete => { - const unlocked = Ra.pets.effarig.level; - const level = Ra.pets.enslaved.level; - if (complete < 1) return `Ra's Effarig Memory level ${unlocked} / ${formatInt(8)}`; - if (level === 25) return `Ra's Enslaved Memories have all been returned`; - return [ - "Ra's Enslaved Memory level", - `${formatInt(level)} / ${formatInt(25)}` - ]; - }, - angle: 142, - diagonal: 85, - horizontal: 16, - }, - }, - connector: { - pathStart: 0.05, - pathEnd: 0.95, - path: new LinearPath(Positions.raReality, Positions.raPetEnslaved), - fill: "#9063de", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "enslaved-pet-to-enslaved": { - visible: () => Ra.has(RA_UNLOCKS.ENSLAVED_UNLOCK), - complete: () => Ra.pets.enslaved.level / 25, - drawOrder: -1, - connector: { - pathStart: 0.05, - pathEnd: 0.55, - path: new LinearPath(Positions.raPetEnslaved, Positions.enslavedReality), - fill: "url(#gradRaEnslaved)", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "v-pet": { - visible: () => Ra.has(RA_UNLOCKS.ENSLAVED_UNLOCK), - complete: () => Ra.pets.enslaved.level / 8, - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.ra.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#9063de", - isStacked: true, - position: Positions.raPetV, - ring: { - rMajor: 12, - }, - legend: { - text: complete => { - const unlocked = Ra.pets.enslaved.level; - const level = Ra.pets.v.level; - if (complete < 1) return `Ra's Enslaved Memory level ${unlocked} / ${formatInt(8)}`; - if (level === 25) return `Ra's V Memories have all been returned`; - return [ - "Ra's V Memory level", - `${formatInt(level)} / ${formatInt(25)}` - ]; - }, - angle: 142, - diagonal: 85, - horizontal: 16, - }, - }, - connector: { - pathStart: 0.05, - pathEnd: 0.95, - path: new LinearPath(Positions.raReality, Positions.raPetV), - fill: "#9063de", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "v-pet-to-v": { - visible: () => Ra.has(RA_UNLOCKS.V_UNLOCK), - complete: () => Ra.pets.v.level / 25, - drawOrder: -1, - connector: { - pathStart: 0.05, - pathEnd: 0.42, - path: new LinearPath(Positions.raPetV, Positions.vUnlockAchievement), - fill: "url(#gradRaV)", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "ra-ring-1": { - visible: () => V.has(V_UNLOCKS.RA_UNLOCK), - complete: () => (V.has(V_UNLOCKS.RA_UNLOCK) ? 1 : 0), - node: { - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#9063de", - position: Positions.raReality, - ring: { - rMajor: 90, - rMinor: 80, - gapCenterDeg: 74, - gapDeg: 268, - }, - } - }, - "ra-ring-2": { - visible: () => V.has(V_UNLOCKS.RA_UNLOCK), - complete: () => (V.has(V_UNLOCKS.RA_UNLOCK) ? 1 : 0), - node: { - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#9063de", - position: Positions.raReality, - ring: { - rMajor: 90, - rMinor: 80, - gapCenterDeg: 161, - gapDeg: 318, - }, - } - }, - "ra-ring-3": { - visible: () => V.has(V_UNLOCKS.RA_UNLOCK), - complete: () => (V.has(V_UNLOCKS.RA_UNLOCK) ? 1 : 0), - node: { - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#9063de", - position: Positions.raReality, - ring: { - rMajor: 90, - rMinor: 80, - gapCenterDeg: 231, - gapDeg: 301, - }, - } - }, - "ra-ring-4": { - visible: () => V.has(V_UNLOCKS.RA_UNLOCK), - complete: () => (V.has(V_UNLOCKS.RA_UNLOCK) ? 1 : 0), - node: { - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#9063de", - position: Positions.raReality, - ring: { - rMajor: 90, - rMinor: 80, - gapCenterDeg: 293, - gapDeg: 334, - }, - } - }, - "ra-ring-5": { - visible: () => V.has(V_UNLOCKS.RA_UNLOCK), - complete: () => (V.has(V_UNLOCKS.RA_UNLOCK) ? 1 : 0), - node: { - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "#9063de", - position: Positions.raReality, - ring: { - rMajor: 90, - rMinor: 80, - gapCenterDeg: -14, - gapDeg: 316, - }, - } - }, - "laitela-unlock": { - visible: () => Ra.has(RA_UNLOCKS.V_UNLOCK), - complete: () => { - if (DarkMatterDimension(1).unlockUpgrade.canBeBought || Laitela.isUnlocked) return 1; - if (MachineHandler.isIMUnlocked) { - if (player.requirementChecks.reality.maxID1.neq(0)) return 0.5; - return Math.clampMax(0.999, player.antimatter.exponent / 1.5e12); - } - return Math.clampMax(0.25, Currency.realityMachines.value.pLog10() / MachineHandler.baseRMCap.exponent); - }, - drawOrder: -1, - node: { - clickAction: () => Tab.celestials.laitela.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - symbol: "ᛝ", - symbolScale: 1.6, - symbolOffset: "0.6", - fill: "white", - position: Positions.laitelaFirstCenter, - ring: { - rMajor: 15, - }, - alwaysShowLegend: true, - legend: { - text: complete => { - const realityName = "Lai'tela's Reality"; - if (complete >= 1) return [realityName]; - - if (!MachineHandler.isIMUnlocked) { - const realityMachines = Currency.realityMachines.value; - const realityMachineCap = MachineHandler.baseRMCap; - return [ - realityName, - "The limits of Reality Machines bind you", - `${format(realityMachines)} / ${format(realityMachineCap)}` - ]; - } - - const hasIDs = player.requirementChecks.reality.maxID1.neq(0); - if (hasIDs) return [ - realityName, - "The Power of Infinity Dimensions", - "blocks your path." - ]; - - const antimatter = Currency.antimatter.value; - const amGoal = DC.E1_5E12; - return [ - realityName, - `${format(antimatter)} / ${format(amGoal)}` - ]; - }, - angle: 260, - diagonal: 15, - horizontal: 8, - }, - }, - connector: { - pathStart: 0.05, - pathEnd: 1, - path: new LinearPath(Positions.raReality, Positions.laitelaFirstCenter), - fill: "url(#gradRaLaitela)", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "laitela-2nd-dim": { - visible: () => Laitela.isUnlocked, - complete: () => { - const upgrade = DarkMatterDimension(2).unlockUpgrade; - if (upgrade.canBeBought || upgrade.isBought) return 1; - if (upgrade.isAvailableForPurchase) return upgrade.currency.value / upgrade.cost; - return Laitela.difficultyTier < 1 - ? 0 - : 30 / player.celestials.laitela.fastestCompletion; - }, - node: { - clickAction: () => Tab.celestials.laitela.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "white", - position: Positions.laitelaFirstLeft, - ring: { - rMajor: 8, - }, - legend: { - text: complete => { - const dmdText = "2nd Dark Matter Dimension"; - const dim = DarkMatterDimension(2); - if (dim.isUnlocked) return [dmdText]; - - const goal = dim.adjustedStartingCost; - if (complete >= 1) return [ - dmdText, - `Dark Matter ${format(Currency.darkMatter.max.min(goal), dim.isUnlocked ? 0 : 2)} / ${format(goal)}` - ]; - - const upgrade = dim.unlockUpgrade; - if (upgrade.isAvailableForPurchase) return [ - dmdText, - `Imaginary Machines - ${format(Math.min(upgrade.currency.value, upgrade.cost), upgrade.canBeBought ? 0 : 2)} - / ${format(upgrade.cost)}` - ]; - - if (player.celestials.laitela.fastestCompletion > 30 && Laitela.difficultyTier < 0) return [ - dmdText, - `Beat Lai'tela's Reality in less that ${format(30)} seconds` - ]; - return [ - dmdText, - `Beat Lai'tela's Reality` - ]; - }, - angle: 135, - diagonal: 30, - horizontal: 16, - }, - }, - connector: { - pathStart: 0.17, - pathEnd: 0.89, - path: new LinearPath(Positions.laitelaFirstCenter, Positions.laitelaFirstLeft), - fill: "white", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "laitela-singularity": { - visible: () => Laitela.isUnlocked, - complete: () => (Currency.singularities.gte(1) - ? 1 - : Math.clampMax(0.999, Currency.darkEnergy.value / Singularity.cap)), - node: { - clickAction: () => Tab.celestials.laitela.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "white", - position: Positions.laitelaFirstRight, - ring: { - rMajor: 8, - }, - legend: { - text: complete => { - if (complete >= 1) return ["Obtain a Singularity"]; - const darkEnergy = Currency.darkEnergy.value; - const singularityGoal = Singularity.cap; - return [ - "Condense your Dark Energy", - "Into a Singularity", - `${format(darkEnergy)} / ${format(singularityGoal)}` - ]; - }, - angle: 45, - diagonal: 65, - horizontal: 16, - }, - }, - connector: { - pathStart: 0.17, - pathEnd: 0.89, - path: new LinearPath(Positions.laitelaFirstCenter, Positions.laitelaFirstRight), - fill: "white", - completeWidth: 6, - incompleteWidth: 4, - } - }, - "laitela-3rd-dim": { - visible: () => DarkMatterDimension(2).isUnlocked && Currency.singularities.gte(1), - complete: () => { - const upgrade = DarkMatterDimension(3).unlockUpgrade; - if (upgrade.canBeBought || upgrade.isBought) return 1; - if (upgrade.isAvailableForPurchase) return upgrade.currency.value / upgrade.cost; - if (!player.auto.singularity.isActive) return 0.5; - return Math.clampMax(0.999, Singularity.singularitiesGained / 20); - }, - node: { - clickAction: () => Tab.celestials.laitela.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "white", - position: Positions.laitelaSecondCenter, - ring: { - rMajor: 8, - }, - legend: { - text: complete => { - const dmdText = "3rd Dark Matter Dimension"; - const dim = DarkMatterDimension(3); - if (dim.isUnlocked) return [dmdText]; - - const goal = dim.adjustedStartingCost; - if (complete >= 1) return [ - dmdText, - `Dark Matter ${format(Currency.darkMatter.max.min(goal), dim.isUnlocked ? 0 : 2)} / ${format(goal)}` - ]; - - const upgrade = dim.unlockUpgrade; - if (upgrade.isAvailableForPurchase) return [ - dmdText, - `Imaginary Machines - ${format(Math.min(upgrade.currency.value, upgrade.cost), upgrade.canBeBought ? 0 : 2)} - / ${format(upgrade.cost)}` - ]; - - if (!player.auto.singularity.isActive) return [ - dmdText, - "Unlock Automatic Singularities", - `${format(Currency.singularities.value)} / ${format(SingularityMilestone.autoCondense.start)}` - ]; - - return [ - dmdText, - `Automatically Condense ${format(20)} Singularities at once`, - `${format(Math.clampMax(Singularity.singularitiesGained, 20))} / ${format(20)}` - ]; - }, - angle: 15, - diagonal: 30, - horizontal: 16, - }, - }, - connector: [ - { - pathStart: 0.10, - pathEnd: 0.89, - path: new LinearPath(Positions.laitelaFirstLeft, Positions.laitelaSecondCenter), - fill: "white", - completeWidth: 6, - incompleteWidth: 4, - }, { - pathStart: 0.10, - pathEnd: 0.89, - path: new LinearPath(Positions.laitelaFirstRight, Positions.laitelaSecondCenter), - fill: "white", - completeWidth: 6, - incompleteWidth: 4, - - }, - ], - }, - "laitela-4th-dim": { - visible: () => DarkMatterDimension(3).isUnlocked, - complete: () => { - const upgrade = DarkMatterDimension(4).unlockUpgrade; - if (upgrade.canBeBought || upgrade.isBought) return 1; - if (upgrade.isAvailableForPurchase) return upgrade.currency.value / upgrade.cost; - return (Replicanti.galaxies.total + player.galaxies + player.dilation.totalTachyonGalaxies) / 80000; - }, - node: { - clickAction: () => Tab.celestials.laitela.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "white", - position: Positions.laitelaSecondLeft, - ring: { - rMajor: 8, - }, - legend: { - text: complete => { - const dmdText = "4th Dark Matter Dimension"; - const dim = DarkMatterDimension(4); - if (dim.isUnlocked) return [dmdText]; - - const goal = dim.adjustedStartingCost; - if (complete >= 1) return [ - dmdText, - `Dark Matter ${format(Currency.darkMatter.max.min(goal), dim.isUnlocked ? 0 : 2)} / ${format(goal)}` - ]; - - const upgrade = dim.unlockUpgrade; - if (upgrade.isAvailableForPurchase) return [ - dmdText, - `Imaginary Machines - ${format(Math.min(upgrade.currency.value, upgrade.cost), upgrade.canBeBought ? 0 : 2)} - / ${format(upgrade.cost)}` - ]; - - const allGalaxies = Replicanti.galaxies.total + player.galaxies + player.dilation.totalTachyonGalaxies; - return [ - dmdText, - `Have ${format(80000)} total Galaxies`, - `${format(Math.clampMax(allGalaxies, 80000))} / ${format(80000)}` - ]; - }, - angle: 135, - diagonal: 30, - horizontal: 16, - }, - }, - connector: { - pathStart: 0.11, - pathEnd: 0.89, - path: new LinearPath(Positions.laitelaSecondCenter, Positions.laitelaSecondLeft), - fill: "white", - completeWidth: 6, - incompleteWidth: 4, - }, - }, - "laitela-annihilation": { - visible: () => DarkMatterDimension(4).isUnlocked, - complete: () => { - const upgrade = ImaginaryUpgrade(19); - if (upgrade.canBeBought || upgrade.isBought) return 1; - if (upgrade.isAvailableForPurchase) return Currency.imaginaryMachines.value / upgrade.cost; - return upgrade.isPossible - ? Tickspeed.continuumValue / 3850000 - : 0; - }, - node: { - clickAction: () => Tab.celestials.laitela.show(true), - incompleteClass: "c-celestial-nav__test-incomplete", - fill: "white", - position: Positions.laitelaSecondRight, - ring: { - rMajor: 8, - }, - legend: { - text: [ - "Annihilate your", - "Dark Matter Dimensions" - ], - angle: 315, - diagonal: 30, - horizontal: 16, - }, - }, - connector: { - pathStart: 0.11, - pathEnd: 0.89, - path: new LinearPath(Positions.laitelaSecondCenter, Positions.laitelaSecondRight), - fill: "white", - completeWidth: 6, - incompleteWidth: 4, - }, - }, - "laitela-destabilization": { - visible: () => DarkMatterDimension(4).isUnlocked && ImaginaryUpgrade(19).isBought, - complete: () => Laitela.difficultyTier / 8, - node: { - incompleteClass: "c-celestial-nav__test-incomplete", - symbol: "ᛝ", - symbolScale: 1.6, - symbolOffset: "0.6", - fill: "white", - position: Positions.laitelaThirdCenter, - ring: { - rMajor: 15, - }, - alwaysShowLegend: true, - legend: { - text: complete => { - if (complete < 1) return [ - "Destabalize Lai'tela's Reality", - "To the point where you cannot", - "use any Dimensions", - `${format(Laitela.difficultyTier)} / ${format(8)}` - ]; - return [ - "Completely destabilized", - "Lai'tela's Reality", - ]; - }, - angle: 0, - diagonal: 15, - horizontal: 8, - }, - }, - connector: [ - { - pathStart: 0.11, - pathEnd: 0.83, - path: new LinearPath(Positions.laitelaSecondLeft, Positions.laitelaThirdCenter), - fill: "white", - completeWidth: 6, - incompleteWidth: 4, - }, { - pathStart: 0.11, - pathEnd: 0.83, - path: new LinearPath(Positions.laitelaSecondRight, Positions.laitelaThirdCenter), - fill: "white", - completeWidth: 6, - incompleteWidth: 4, - } - ] - }, + connector: pelleStarConnector(index, connectorFill, fillType === FILL_STATE.OVERFILL), }; -}()); +} + +// Slightly reduces boilerplate; there are a total of 15 rift elements which are largely duplicated code +const fillStates = ["fill", "drain", "overfill"]; +const riftNames = ["Vacuum", "Decay", "Chaos", "Recursion", "Paradox"]; +const angles = [225, 315, 45, 135, 135]; +const riftFillElements = {}; +for (const fill of fillStates) { + for (let index = 0; index < riftNames.length; index++) { + const name = riftNames[index]; + riftFillElements[`pelle-${name}-${fill}`] = pelleRiftFill(name, index, angles[index], + FILL_STATE[fill.toUpperCase()]); + } +} + +GameDatabase.celestials.navigation = { + "teresa-base": { + visible: () => true, + complete: () => 1, + node: { + clickAction: () => Tab.celestials.teresa.show(true), + completeClass: "c-celestial-nav__test-complete", + incompleteClass: "c-celestial-nav__test-incomplete", + position: Positions.teresa, + ring: { + rMajor: 78, + rMinor: 64, + }, + legend: { + text: "Teresa", + angle: 135, + diagonal: 32, + horizontal: 16, + }, + }, + }, + "teresa-reality-unlock": { + visible: () => true, + complete: () => (TeresaUnlocks.run.canBeApplied + ? 1 : Decimal.pLog10(Teresa.pouredAmount) / Math.log10(TeresaUnlocks.run.price)), + node: { + completeClass: "c-celestial-nav__test-complete", + incompleteClass: "c-celestial-nav__test-incomplete", + position: Positions.teresa, + ring: { + rMajor: 32, + rMinor: 22, + }, + legend: { + hideWhenCompleted: true, + text: () => { + const rm = Teresa.pouredAmount; + const cost = TeresaUnlocks.run.price; + return `Pour ${format(rm, 2)} / ${format(cost, 2)} RM`; + }, + angle: 135, + diagonal: 16, + horizontal: 16, + }, + }, + connector: (function() { + const pathStart = -Math.PI; + const pathEnd = Math.PI; + const path = LogarithmicSpiral.fromPolarEndpoints(Positions.teresa, -Math.PI, 69, Math.PI, 26); + const pathPadStart = path.angleFromRadius(64 - 3) - pathStart; + const pathPadEnd = pathEnd - path.angleFromRadius(34); + return { + pathStart, + pathEnd, + path, + pathPadStart, + pathPadEnd, + }; + }()), + }, + "teresa-reality": { + visible: () => true, + complete: () => (Teresa.runCompleted ? 1 : 0), + node: { + clickAction: () => Tab.celestials.teresa.show(true), + completeClass: "c-celestial-nav__test-complete", + incompleteClass: "c-celestial-nav__test-incomplete", + symbol: "Ϟ", + position: Positions.teresa, + ring: { + rMajor: 16, + }, + alwaysShowLegend: true, + legend: { + text: "Teresa's Reality", + angle: -135, + diagonal: 96, + horizontal: 16, + }, + } + }, + "teresa-pp-shop": { + visible: () => true, + complete: () => (TeresaUnlocks.shop.canBeApplied + ? 1 : Decimal.pLog10(Teresa.pouredAmount) / Math.log10(TeresaUnlocks.shop.price)), + node: { + clickAction: () => Tab.celestials.teresa.show(true), + completeClass: "c-celestial-nav__test-complete", + incompleteClass: "c-celestial-nav__test-incomplete", + position: Positions.teresaPerkPointShop, + ring: { + rMajor: 16, + rMinor: 0, + }, + legend: { + text: complete => { + if (complete >= 1) return "Perk Point Shop"; + const rm = Teresa.pouredAmount; + const cost = TeresaUnlocks.shop.price; + return [ + "Perk Point Shop", + `Pour ${format(rm, 2)} / ${format(cost, 2)} Reality Machines` + ]; + }, + angle: -35, + diagonal: 16, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: LinearPath.connectCircles(Positions.teresa, 78 - 1, Positions.teresaPerkPointShop, 16 - 1), + completeWidth: 6, + incompleteWidth: 4, + } + }, + "effarig-shop": { + visible: () => true, + complete: () => (TeresaUnlocks.effarig.canBeApplied + ? 1 : Decimal.pLog10(Teresa.pouredAmount) / Math.log10(TeresaUnlocks.effarig.price)), + node: { + clickAction: () => Tab.celestials.effarig.show(true), + completeClass: "c-celestial-nav__effarig", + incompleteClass: "c-celestial-nav__test-incomplete", + position: Positions.effarigShop, + ring: { + rMajor: 24, + }, + legend: { + text: complete => { + if (complete >= 1) return "Effarig's Shop"; + const rm = Teresa.pouredAmount; + const cost = TeresaUnlocks.effarig.price; + return [ + "Effarig", + `Pour ${format(rm, 2)} / ${format(cost, 2)} Reality Machines` + ]; + }, + angle: -135, + diagonal: 16, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: LinearPath.connectCircles(Positions.teresa, 78 - 1, Positions.effarigShop, 24 - 1), + fill: "url(#gradTeresaEffarig)", + } + }, + "effarig-reality-unlock": { + visible: () => TeresaUnlocks.effarig.canBeApplied, + // If the upgrade to unlock the reality isn't yet bought, clamp the progress at 99.9%, + // even if the player has enough relic shards to buy it. + complete: () => (EffarigUnlock.run.isUnlocked + ? 1 : Math.clampMax(0.999, Decimal.pLog10(Currency.relicShards.value) / + Math.log10(EffarigUnlock.run.cost))), + node: { + clickAction: () => Tab.celestials.effarig.show(true), + completeClass: "c-celestial-nav__effarig", + incompleteClass: "c-celestial-nav__test-incomplete", + position: Positions.effarigRealityUnlock, + ring: { + rMajor: 16, + }, + legend: { + text: complete => { + if (complete >= 1) return "Unlock Effarig's Reality"; + const rs = Currency.relicShards.value; + const cost = EffarigUnlock.run.cost; + return [ + "Unlock Effarig's Reality", + `Reach ${format(rs, 2)} / ${format(cost, 2)} Relic Shards` + ]; + }, + angle: 75, + diagonal: 40, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: LinearPath.connectCircles(Positions.effarigShop, 24 - 1, Positions.effarigRealityUnlock, 16 - 1), + fill: "#d13737", + } + }, + "effarig-infinity": { + visible: () => EffarigUnlock.run.isUnlocked, + complete: () => { + if (EffarigUnlock.infinity.isUnlocked) return 1; + if (!Effarig.isRunning) return 0; + + return Currency.antimatter.value.pLog10() / Decimal.NUMBER_MAX_VALUE.log10(); + }, + node: { + clickAction: () => Tab.celestials.effarig.show(true), + completeClass: "c-celestial-nav__effarig", + incompleteClass: "c-celestial-nav__test-incomplete", + position: Positions.effarigNode, + ring: { + rMajor: 60, + rMinor: 52, + }, + legend: { + text: complete => { + if (complete >= 1) return "Effarig's Infinity"; + if (complete === 0) return "Unlock Effarig's Reality"; + const am = Effarig.isRunning ? Currency.antimatter.value : 0; + return [ + "Effarig's Infinity", + `Reach ${format(am, 2)} / ${format(Number.MAX_VALUE, 2)}`, + "Antimatter inside Effarig's Reality." + ]; + }, + angle: 0, + diagonal: 100, + horizontal: 16, + }, + bgDrawOrder: CELESTIAL_NAV_DRAW_ORDER.NODE_BG + 750, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: LinearPath.connectCircles(Positions.effarigRealityUnlock, 16 - 1, Positions.effarigNode, 60 - 1), + fill: "#d13737", + } + }, + "effarig-eternity": { + visible: () => EffarigUnlock.infinity.isUnlocked, + complete: () => { + if (EffarigUnlock.eternity.isUnlocked) return 1; + if (!Effarig.isRunning) return 0; + + return Currency.infinityPoints.value.pLog10() / Decimal.NUMBER_MAX_VALUE.log10(); + }, + node: { + clickAction: () => Tab.celestials.effarig.show(true), + completeClass: "c-celestial-nav__effarig", + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#7131ec", + position: Positions.effarigNode, + ring: { + rMajor: 40, + rMinor: 30, + }, + legend: { + text: complete => { + if (complete >= 1) return "Effarig's Eternity"; + const ip = Effarig.isRunning ? Currency.infinityPoints.value : 0; + return [ + "Effarig's Eternity", + `Reach ${format(ip, 2)} / ${format(Number.MAX_VALUE, 2)}`, + "Infinity Points inside Effarig's Reality." + ]; + }, + angle: -45, + diagonal: 16, + horizontal: 16, + }, + }, + connector: (function() { + const pathStart = -Math.PI; + const pathEnd = 0; + const path = LogarithmicSpiral.fromPolarEndpoints(new Vector(560, 25), pathStart, 66, pathEnd, 26); + const pathPadStart = 0; + const pathPadEnd = pathEnd - path.angleFromRadius(30); + return { + pathStart, + pathEnd, + path, + pathPadStart, + pathPadEnd, + fill: "#d13737" + }; + }()) + }, + "effarig-reality": { + visible: () => EffarigUnlock.eternity.isUnlocked, + complete: () => { + if (EffarigUnlock.reality.isUnlocked) return 1; + if (!Effarig.isRunning) return 0; + + return Currency.eternityPoints.value.pLog10() / 4000; + }, + node: { + clickAction: () => Tab.celestials.effarig.show(true), + completeClass: "c-celestial-nav__effarig", + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#A101ec", + position: new Vector(550, 25), + ring: { + rMajor: 20, + rMinor: 0, + }, + symbol: "Ϙ", + alwaysShowLegend: true, + legend: { + text: complete => { + if (complete >= 1) return "Effarig's Reality"; + const ep = Effarig.isRunning ? Currency.eternityPoints.value : 0; + const goal = DC.E4000; + return [ + "Effarig's Reality", + `Reach ${format(ep, 2)} / ${format(goal, 2)}`, + "Eternity Points inside Effarig's Reality." + ]; + }, + angle: -120, + diagonal: 82, + horizontal: 16, + }, + }, + connector: (function() { + const pathStart = 0; + const pathEnd = Math.PI; + const path = LogarithmicSpiral.fromPolarEndpoints(new Vector(558, 25), pathStart, 26, pathEnd, 24); + const pathPadStart = 0; + const pathPadEnd = 0; + return { + pathStart, + pathEnd, + path, + pathPadStart, + pathPadEnd, + fill: "#d13737" + }; + }()) + }, + "enslaved": { + visible: () => EffarigUnlock.eternity.isUnlocked, + complete: () => (EffarigUnlock.eternity.isUnlocked ? 1 : 0), + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.enslaved.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffa337", + position: Positions.enslavedReality, + ring: { + rMajor: 80, + rMinor: 70, + gapCenterDeg: 15, + gapDeg: 200, + }, + alwaysShowLegend: false, + legend: { + text: "Nameless", + angle: -90, + diagonal: 20, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + drawOrder: CELESTIAL_NAV_DRAW_ORDER.NODE_BG + 500, + path: LinearPath.connectCircles(Positions.effarigNode, 40 - 1, Positions.enslavedReality, 80 - 1), + fill: "url(#gradEffarigEnslaved)", + } + }, + "enslaved-unlock-glyph-level": { + visible: () => EffarigUnlock.eternity.isUnlocked, + complete: () => player.records.bestReality.glyphLevel / 5000, + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.enslaved.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffa337", + position: Positions.enslavedGlyphLevel, + ring: { + rMajor: 24, + rMinor: 16, + gapCenterDeg: 40, + gapDeg: 60, + gapAngleDeg: 0, + }, + legend: { + text: complete => { + if (complete >= 1) return "Broken the chain with Glyph level"; + const goal = 5000; + return [ + "Break a chain", + `Reach Glyph level ${formatInt(Math.min(player.records.bestReality.glyphLevel, goal))}/${formatInt(goal)}` + ]; + }, + angle: -45, + diagonal: 16, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath( + new Vector(650 - 74 * Math.sqrt(0.75), 250 - 74 * 0.5), + Positions.enslavedGlyphLevel) + .trimEnd(23), + fill: "#ffa337", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "enslaved-unlock-glyph-rarity": { + visible: () => EffarigUnlock.eternity.isUnlocked, + complete: () => { + const bestRarity = strengthToRarity(player.records.bestReality.glyphStrength); + return bestRarity / 100; + }, + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.enslaved.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffa337", + position: Positions.enslavedGlyphRarity, + ring: { + rMajor: 24, + rMinor: 16, + gapCenterDeg: 220, + gapDeg: 60, + gapAngleDeg: 0, + }, + legend: { + text: complete => { + if (complete >= 1) return "Broken the chain with Glyph rarity"; + const goal = 100; + return [ + "Break a chain", + `Reach Glyph rarity ${formatPercents(complete * goal / 100, 1)}/${formatPercents(goal / 100, 1)}` + ]; + }, + angle: 135, + diagonal: 32, + horizontal: 32, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.enslavedGlyphRarity, Positions.enslavedGlyphLevel).trimStart(23).trimEnd(23), + fill: "#ffa337", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "enslaved-reality": { + visible: () => EffarigUnlock.eternity.isUnlocked, + complete: () => { + if (Enslaved.isCompleted) return 1; + if (!Enslaved.isRunning) return 0; + + return Currency.eternityPoints.value.pLog10() / 4000; + }, + node: { + clickAction: () => Tab.celestials.enslaved.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffa337", + position: Positions.enslavedReality, + ring: { + rMajor: 80, + rMinor: 70, + gapCenterDeg: 195, + gapDeg: 200, + }, + alwaysShowLegend: true, + legend: { + text: complete => { + if (complete >= 1) return "The Nameless Ones' Reality"; + const ep = Enslaved.isRunning ? Currency.eternityPoints.value : 0; + const goal = DC.E4000; + return [ + "The Nameless Ones' Reality", + `Reach ${format(ep, 2)} / ${format(goal, 2)}`, + "Eternity Points inside The Nameless Ones' Reality." + ]; + }, + angle: 45, + diagonal: 16, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.enslavedGlyphRarity, new Vector(650 + 74 * Math.sqrt(0.75), 250 + 74 * 0.5)) + .trimStart(23), + fill: "#ffa337", + } + }, + "v-unlock-achievement": { + visible: () => EffarigUnlock.reality.isUnlocked, + complete: () => { + if (Achievement(151).isUnlocked) return 1; + if (!player.requirementChecks.infinity.noAD8) return 0; + + return player.galaxies / 800; + }, + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.v.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + symbol: "⌬", + symbolOffset: "2", + fill: "#ffe066", + position: Positions.vUnlockAchievement, + ring: { + rMajor: 20, + }, + alwaysShowLegend: true, + legend: { + text: complete => { + const goal = 800; + if (complete >= 1) return "V's Reality"; + const galaxies = player.requirementChecks.infinity.noAD8 ? player.galaxies : 0; + return [ + "V's unlock Achievement", + `Reach ${formatInt(galaxies)} / ${formatInt(goal)} Antimatter Galaxies without buying`, + "8th Antimatter Dimensions in your current Infinity" + ]; + }, + angle: 135, + diagonal: 60, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: LinearPath.connectCircles(Positions.enslavedReality, 80 - 1, Positions.vUnlockAchievement, 16 - 1), + fill: "url(#gradEnslavedV)", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "v-unlock-1": { + visible: () => Achievement(151).isUnlocked || VUnlocks.vAchievementUnlock.isUnlocked, + complete: () => vUnlockProgress(1), + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.v.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffe066", + position: Positions.vAchievement1, + ring: { + rMajor: 8, + }, + legend: { + text: complete => vUnlockLegendLabel(complete, 1), + angle: -135, + diagonal: 16, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.vUnlockAchievement, Positions.vAchievement1), + fill: "#ffe066", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "v-unlock-2": { + visible: () => Achievement(151).isUnlocked || VUnlocks.vAchievementUnlock.isUnlocked, + complete: () => vUnlockProgress(2), + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.v.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffe066", + position: Positions.vAchievement2, + ring: { + rMajor: 8, + }, + legend: { + text: complete => vUnlockLegendLabel(complete, 2), + angle: -135, + diagonal: 30, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.vUnlockAchievement, Positions.vAchievement2), + fill: "#ffe066", + completeWidth: 6, + incompleteWidth: 4, + } + }, + + "v-unlock-3": { + visible: () => Achievement(151).isUnlocked || VUnlocks.vAchievementUnlock.isUnlocked, + complete: () => vUnlockProgress(3), + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.v.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffe066", + position: Positions.vAchievement3, + ring: { + rMajor: 8, + }, + legend: { + text: complete => vUnlockLegendLabel(complete, 3), + angle: -135, + diagonal: 45, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.vUnlockAchievement, Positions.vAchievement3), + fill: "#ffe066", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "v-unlock-4": { + visible: () => Achievement(151).isUnlocked || VUnlocks.vAchievementUnlock.isUnlocked, + complete: () => vUnlockProgress(4), + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.v.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffe066", + position: Positions.vAchievement4, + ring: { + rMajor: 8, + }, + legend: { + text: complete => vUnlockLegendLabel(complete, 4), + angle: -135, + diagonal: 60, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.vUnlockAchievement, Positions.vAchievement4), + fill: "#ffe066", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "v-unlock-5": { + visible: () => Achievement(151).isUnlocked || VUnlocks.vAchievementUnlock.isUnlocked, + complete: () => vUnlockProgress(5), + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.v.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffe066", + position: Positions.vAchievement5, + ring: { + rMajor: 8, + }, + legend: { + text: complete => vUnlockLegendLabel(complete, 5), + angle: -135, + diagonal: 75, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.vUnlockAchievement, Positions.vAchievement5), + fill: "#ffe066", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "v-unlock-6": { + visible: () => Achievement(151).isUnlocked || VUnlocks.vAchievementUnlock.isUnlocked, + complete: () => vUnlockProgress(6), + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.v.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffe066", + position: Positions.vAchievement0, + ring: { + rMajor: 8, + }, + legend: { + text: complete => vUnlockLegendLabel(complete, 6), + angle: -135, + diagonal: 90, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.vUnlockAchievement, Positions.vAchievement0), + fill: "#ffe066", + completeWidth: 6, + incompleteWidth: 4, + } + }, + + "v-achievement-0": { + visible: () => VUnlocks.vAchievementUnlock.isUnlocked, + complete: () => VRunUnlocks.all[0].completions / 6, + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.v.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffe066", + position: Positions.vAchievement0, + ring: { + rMajor: 8, + }, + legend: { + text: complete => { + const name = VRunUnlocks.all[0].config.name; + if (complete >= 1) return `V-Achievement "${name}"`; + const completions = VRunUnlocks.all[0].completions; + return [ + "V-Achievement", + `Reach ${formatInt(completions)} / ${formatInt(6)} completions in ${name}.` + ]; + }, + angle: -135, + diagonal: 16, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.vAchievement5, Positions.vAchievement0), + fill: "#ffe066", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "v-achievement-1": { + visible: () => VUnlocks.vAchievementUnlock.isUnlocked, + complete: () => VRunUnlocks.all[1].completions / 6, + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.v.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffe066", + position: Positions.vAchievement1, + ring: { + rMajor: 8, + }, + legend: { + text: complete => { + const name = VRunUnlocks.all[1].config.name; + if (complete >= 1) return `V-Achievement "${name}"`; + const completions = VRunUnlocks.all[1].completions; + return [ + "V-Achievement", + `Reach ${formatInt(completions)} / ${formatInt(6)} completions in ${name}.` + ]; + }, + angle: 20, + diagonal: 16, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.vAchievement0, Positions.vAchievement1), + fill: "#ffe066", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "v-achievement-2": { + visible: () => VUnlocks.vAchievementUnlock.isUnlocked, + complete: () => VRunUnlocks.all[2].completions / 6, + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.v.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffe066", + position: Positions.vAchievement2, + ring: { + rMajor: 8, + }, + legend: { + text: complete => { + const name = VRunUnlocks.all[2].config.name; + if (complete >= 1) return `V-Achievement "${name}"`; + const completions = VRunUnlocks.all[2].completions; + return [ + "V-Achievement", + `Reach ${formatInt(completions)} / ${formatInt(6)} completions in ${name}.` + ]; + }, + angle: 315, + diagonal: 25, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.vAchievement1, Positions.vAchievement2), + fill: "#ffe066", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "v-achievement-3": { + visible: () => VUnlocks.vAchievementUnlock.isUnlocked, + complete: () => VRunUnlocks.all[3].completions / 6, + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.v.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffe066", + position: Positions.vAchievement3, + ring: { + rMajor: 8, + }, + legend: { + text: complete => { + const name = VRunUnlocks.all[3].config.name; + if (complete >= 1) return `V-Achievement "${name}"`; + const completions = VRunUnlocks.all[3].completions; + return [ + "V-Achievement", + `Reach ${formatInt(completions)} / ${formatInt(6)} completions in ${name}.` + ]; + }, + angle: 135, + diagonal: 25, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.vAchievement2, Positions.vAchievement3), + fill: "#ffe066", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "v-achievement-4": { + visible: () => VUnlocks.vAchievementUnlock.isUnlocked, + complete: () => VRunUnlocks.all[4].completions / 6, + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.v.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffe066", + position: Positions.vAchievement4, + ring: { + rMajor: 8, + }, + legend: { + text: complete => { + const name = VRunUnlocks.all[4].config.name; + if (complete >= 1) return `V-Achievement "${name}"`; + const completions = VRunUnlocks.all[4].completions; + return [ + "V-Achievement", + `Reach ${formatInt(completions)} / ${formatInt(6)} completions in ${name}.` + ]; + }, + angle: 60, + diagonal: 25, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.vAchievement3, Positions.vAchievement4), + fill: "#ffe066", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "v-achievement-5": { + visible: () => VUnlocks.vAchievementUnlock.isUnlocked, + complete: () => VRunUnlocks.all[5].completions / 6, + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.v.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#ffe066", + position: Positions.vAchievement5, + ring: { + rMajor: 8, + }, + legend: { + text: complete => { + const name = VRunUnlocks.all[5].config.name; + if (complete >= 1) return `V-Achievement "${name}"`; + const completions = VRunUnlocks.all[5].completions; + return [ + "V-Achievement", + `Reach ${formatInt(completions)} / ${formatInt(6)} completions in ${name}.` + ]; + }, + angle: 260, + diagonal: 30, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.vAchievement4, Positions.vAchievement5), + fill: "#ffe066", + completeWidth: 6, + incompleteWidth: 4, + } + }, + + "ra": { + visible: () => VUnlocks.raUnlock.isUnlocked, + complete: () => (VUnlocks.raUnlock.isUnlocked ? 1 : 0), + node: { + clickAction: () => Tab.celestials.ra.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + symbol: "\uf185", + symbolOffset: "2", + fill: "#9063de", + position: Positions.raReality, + ring: { + rMajor: 24, + }, + alwaysShowLegend: true, + legend: { + text: "Ra's Reality", + angle: 230, + diagonal: 85, + horizontal: 16, + }, + } + }, + "teresa-pet": { + visible: () => VUnlocks.raUnlock.isUnlocked, + complete: () => (VUnlocks.raUnlock.isUnlocked ? 1 : 0), + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.ra.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#9063de", + isStacked: true, + position: Positions.raPetTeresa, + ring: { + rMajor: 12, + }, + legend: { + text: () => { + const level = Ra.pets.teresa.level; + if (level === 25) return `Ra's Teresa Memories have all been returned`; + return [ + "Ra's Teresa Memory level", + `${formatInt(level)} / ${formatInt(25)}` + ]; + }, + angle: 142, + diagonal: 85, + horizontal: 16, + }, + }, + connector: { + pathStart: 0.05, + pathEnd: 0.95, + path: new LinearPath(Positions.raReality, Positions.raPetTeresa), + fill: "#9063de", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "teresa-pet-to-teresa": { + visible: () => VUnlocks.raUnlock.isUnlocked, + complete: () => Ra.pets.teresa.level / 25, + drawOrder: -1, + connector: { + pathStart: 0.05, + pathEnd: 0.70, + path: new LinearPath(Positions.raPetTeresa, Positions.teresa), + fill: "url(#gradRaTeresa)", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "effarig-pet": { + visible: () => VUnlocks.raUnlock.isUnlocked, + complete: () => Ra.pets.teresa.level / 8, + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.ra.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#9063de", + isStacked: true, + position: Positions.raPetEffarig, + ring: { + rMajor: 12, + }, + legend: { + text: complete => { + const unlocked = Ra.pets.teresa.level; + const level = Ra.pets.effarig.level; + if (complete < 1) return `Ra's Teresa Memory level ${unlocked} / ${formatInt(8)}`; + if (level === 25) return `Ra's Effarig Memories have all been returned`; + return [ + "Ra's Effarig Memory level", + `${formatInt(level)} / ${formatInt(25)}` + ]; + }, + angle: 142, + diagonal: 85, + horizontal: 16, + }, + }, + connector: { + pathStart: 0.05, + pathEnd: 0.95, + path: new LinearPath(Positions.raReality, Positions.raPetEffarig), + fill: "#9063de", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "effarig-pet-to-effarig": { + visible: () => Ra.unlocks.effarigUnlock.isUnlocked, + complete: () => Ra.pets.effarig.level / 25, + drawOrder: -1, + connector: { + pathStart: 0.05, + pathEnd: 0.60, + path: new LinearPath(Positions.raPetEffarig, Positions.effarigNode), + fill: "url(#gradRaEffarig)", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "enslaved-pet": { + visible: () => Ra.unlocks.effarigUnlock.isUnlocked, + complete: () => Ra.pets.effarig.level / 8, + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.ra.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#9063de", + isStacked: true, + position: Positions.raPetEnslaved, + ring: { + rMajor: 12, + }, + legend: { + text: complete => { + const unlocked = Ra.pets.effarig.level; + const level = Ra.pets.enslaved.level; + if (complete < 1) return `Ra's Effarig Memory level ${unlocked} / ${formatInt(8)}`; + if (level === 25) return `Ra's Nameless Memories have all been returned`; + return [ + "Ra's Nameless Memory level", + `${formatInt(level)} / ${formatInt(25)}` + ]; + }, + angle: 142, + diagonal: 85, + horizontal: 16, + }, + }, + connector: { + pathStart: 0.05, + pathEnd: 0.95, + path: new LinearPath(Positions.raReality, Positions.raPetEnslaved), + fill: "#9063de", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "enslaved-pet-to-enslaved": { + visible: () => Ra.unlocks.enslavedUnlock.isUnlocked, + complete: () => Ra.pets.enslaved.level / 25, + drawOrder: -1, + connector: { + pathStart: 0.05, + pathEnd: 0.55, + path: new LinearPath(Positions.raPetEnslaved, Positions.enslavedReality), + fill: "url(#gradRaEnslaved)", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "v-pet": { + visible: () => Ra.unlocks.enslavedUnlock.isUnlocked, + complete: () => Ra.pets.enslaved.level / 8, + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.ra.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#9063de", + isStacked: true, + position: Positions.raPetV, + ring: { + rMajor: 12, + }, + legend: { + text: complete => { + const unlocked = Ra.pets.enslaved.level; + const level = Ra.pets.v.level; + if (complete < 1) return `Ra's Nameless Memory level ${unlocked} / ${formatInt(8)}`; + if (level === 25) return `Ra's V Memories have all been returned`; + return [ + "Ra's V Memory level", + `${formatInt(level)} / ${formatInt(25)}` + ]; + }, + angle: 142, + diagonal: 85, + horizontal: 16, + }, + }, + connector: { + pathStart: 0.05, + pathEnd: 0.95, + path: new LinearPath(Positions.raReality, Positions.raPetV), + fill: "#9063de", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "v-pet-to-v": { + visible: () => Ra.unlocks.vUnlock.isUnlocked, + complete: () => Ra.pets.v.level / 25, + drawOrder: -1, + connector: { + pathStart: 0.05, + pathEnd: 0.42, + path: new LinearPath(Positions.raPetV, Positions.vUnlockAchievement), + fill: "url(#gradRaV)", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "ra-ring-1": { + visible: () => VUnlocks.raUnlock.isUnlocked, + complete: () => (VUnlocks.raUnlock.isUnlocked ? 1 : 0), + node: { + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#9063de", + position: Positions.raReality, + ring: { + rMajor: 90, + rMinor: 80, + gapCenterDeg: 74, + gapDeg: 268, + }, + } + }, + "ra-ring-2": { + visible: () => VUnlocks.raUnlock.isUnlocked, + complete: () => (VUnlocks.raUnlock.isUnlocked ? 1 : 0), + node: { + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#9063de", + position: Positions.raReality, + ring: { + rMajor: 90, + rMinor: 80, + gapCenterDeg: 161, + gapDeg: 318, + }, + } + }, + "ra-ring-3": { + visible: () => VUnlocks.raUnlock.isUnlocked, + complete: () => (VUnlocks.raUnlock.isUnlocked ? 1 : 0), + node: { + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#9063de", + position: Positions.raReality, + ring: { + rMajor: 90, + rMinor: 80, + gapCenterDeg: 231, + gapDeg: 301, + }, + } + }, + "ra-ring-4": { + visible: () => VUnlocks.raUnlock.isUnlocked, + complete: () => (VUnlocks.raUnlock.isUnlocked ? 1 : 0), + node: { + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#9063de", + position: Positions.raReality, + ring: { + rMajor: 90, + rMinor: 80, + gapCenterDeg: 293, + gapDeg: 334, + }, + } + }, + "ra-ring-5": { + visible: () => VUnlocks.raUnlock.isUnlocked, + complete: () => (VUnlocks.raUnlock.isUnlocked ? 1 : 0), + node: { + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "#9063de", + position: Positions.raReality, + ring: { + rMajor: 90, + rMinor: 80, + gapCenterDeg: -14, + gapDeg: 316, + }, + } + }, + "laitela-unlock": { + visible: () => Ra.unlocks.vUnlock.isUnlocked, + complete: () => { + if (DarkMatterDimension(1).unlockUpgrade.canBeBought || Laitela.isUnlocked) return 1; + if (MachineHandler.isIMUnlocked) { + if (player.requirementChecks.reality.maxID1.neq(0)) return 0.5; + return 0.5 + 0.5 * Math.clampMax(0.999, player.antimatter.exponent / 1.5e12); + } + return Math.clampMax(0.5, Currency.realityMachines.value.pLog10() / MachineHandler.baseRMCap.exponent); + }, + drawOrder: -1, + node: { + clickAction: () => Tab.celestials.laitela.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + symbol: "ᛝ", + symbolScale: 1.6, + symbolOffset: "0.6", + fill: "white", + position: Positions.laitelaFirstCenter, + ring: { + rMajor: 15, + }, + alwaysShowLegend: true, + legend: { + text: complete => { + const realityName = "Lai'tela's Reality"; + if (complete >= 1) return [realityName]; + + if (!MachineHandler.isIMUnlocked) { + const realityMachines = Currency.realityMachines.value; + const realityMachineCap = MachineHandler.baseRMCap; + return [ + realityName, + "The limits of Reality Machines bind you", + `${format(realityMachines)} / ${format(realityMachineCap)}` + ]; + } + + const hasIDs = player.requirementChecks.reality.maxID1.neq(0); + if (hasIDs) return [ + realityName, + "The Power of Infinity Dimensions", + "blocks your path." + ]; + + const antimatter = Currency.antimatter.value; + const amGoal = DC.E1_5E12; + return [ + realityName, + `${format(antimatter)} / ${format(amGoal)}` + ]; + }, + angle: 260, + diagonal: 15, + horizontal: 8, + }, + }, + connector: { + pathStart: 0.05, + pathEnd: 1, + path: new LinearPath(Positions.raReality, Positions.laitelaFirstCenter), + fill: "url(#gradRaLaitela)", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "laitela-2nd-dim": { + visible: () => Laitela.isUnlocked, + complete: () => { + const upgrade = DarkMatterDimension(2).unlockUpgrade; + if (upgrade.canBeBought || upgrade.isBought) return 1; + if (upgrade.isAvailableForPurchase) return upgrade.currency.value / upgrade.cost; + return Laitela.difficultyTier < 1 + ? 0 + : 30 / player.celestials.laitela.fastestCompletion; + }, + node: { + clickAction: () => Tab.celestials.laitela.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "white", + position: Positions.laitelaFirstLeft, + ring: { + rMajor: 8, + }, + legend: { + text: complete => { + const dmdText = "2nd Dark Matter Dimension"; + const dim = DarkMatterDimension(2); + if (dim.isUnlocked) return [dmdText]; + + const goal = dim.adjustedStartingCost; + if (complete >= 1) return [ + dmdText, + `Dark Matter ${format(Currency.darkMatter.max.min(goal), dim.isUnlocked ? 0 : 2)} / ${format(goal)}` + ]; + + const upgrade = dim.unlockUpgrade; + if (upgrade.isAvailableForPurchase) return [ + dmdText, + `Imaginary Machines + ${format(Math.min(upgrade.currency.value, upgrade.cost), upgrade.canBeBought ? 0 : 2)} + / ${format(upgrade.cost)}` + ]; + + if (player.celestials.laitela.fastestCompletion > 30 && Laitela.difficultyTier < 0) return [ + dmdText, + `Beat Lai'tela's Reality in less that ${format(30)} seconds` + ]; + return [ + dmdText, + `Beat Lai'tela's Reality` + ]; + }, + angle: 135, + diagonal: 30, + horizontal: 16, + }, + }, + connector: { + pathStart: 0.17, + pathEnd: 0.89, + path: new LinearPath(Positions.laitelaFirstCenter, Positions.laitelaFirstLeft), + fill: "white", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "laitela-singularity": { + visible: () => Laitela.isUnlocked, + complete: () => (Currency.singularities.gte(1) + ? 1 + : Math.clampMax(0.999, Currency.darkEnergy.value / Singularity.cap)), + node: { + clickAction: () => Tab.celestials.laitela.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "white", + position: Positions.laitelaFirstRight, + ring: { + rMajor: 8, + }, + legend: { + text: complete => { + if (complete >= 1) return ["Obtain a Singularity"]; + const darkEnergy = Currency.darkEnergy.value; + const singularityGoal = Singularity.cap; + return [ + "Condense your Dark Energy", + "Into a Singularity", + `${format(darkEnergy)} / ${format(singularityGoal)}` + ]; + }, + angle: 45, + diagonal: 65, + horizontal: 16, + }, + }, + connector: { + pathStart: 0.17, + pathEnd: 0.89, + path: new LinearPath(Positions.laitelaFirstCenter, Positions.laitelaFirstRight), + fill: "white", + completeWidth: 6, + incompleteWidth: 4, + } + }, + "laitela-3rd-dim": { + visible: () => DarkMatterDimension(2).isUnlocked && Currency.singularities.gte(1), + complete: () => { + const upgrade = DarkMatterDimension(3).unlockUpgrade; + if (upgrade.canBeBought || upgrade.isBought) return 1; + if (upgrade.isAvailableForPurchase) return upgrade.currency.value / upgrade.cost; + if (!player.auto.singularity.isActive) return 0.5; + return Math.clampMax(0.999, Singularity.singularitiesGained / 20); + }, + node: { + clickAction: () => Tab.celestials.laitela.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "white", + position: Positions.laitelaSecondCenter, + ring: { + rMajor: 15, + }, + legend: { + text: complete => { + const dmdText = "3rd Dark Matter Dimension"; + const dim = DarkMatterDimension(3); + if (dim.isUnlocked) return [dmdText]; + + const goal = dim.adjustedStartingCost; + if (complete >= 1) return [ + dmdText, + `Dark Matter ${format(Currency.darkMatter.max.min(goal), dim.isUnlocked ? 0 : 2)} / ${format(goal)}` + ]; + + const upgrade = dim.unlockUpgrade; + if (upgrade.isAvailableForPurchase) return [ + dmdText, + `Imaginary Machines + ${format(Math.min(upgrade.currency.value, upgrade.cost), upgrade.canBeBought ? 0 : 2)} + / ${format(upgrade.cost)}` + ]; + + if (!player.auto.singularity.isActive) return [ + dmdText, + "Unlock Automatic Singularities", + `${format(Currency.singularities.value)} / ${format(SingularityMilestone.autoCondense.start)}` + ]; + + return [ + dmdText, + `Automatically Condense ${format(20)} Singularities at once`, + `${format(Math.clampMax(Singularity.singularitiesGained, 20))} / ${format(20)}` + ]; + }, + angle: 15, + diagonal: 30, + horizontal: 16, + }, + }, + connector: [ + { + pathStart: 0.10, + pathEnd: 0.89, + path: new LinearPath(Positions.laitelaFirstLeft, Positions.laitelaSecondCenter), + fill: "white", + completeWidth: 6, + incompleteWidth: 4, + }, { + pathStart: 0.10, + pathEnd: 0.89, + path: new LinearPath(Positions.laitelaFirstRight, Positions.laitelaSecondCenter), + fill: "white", + completeWidth: 6, + incompleteWidth: 4, + + }, + ], + }, + "laitela-4th-dim": { + visible: () => DarkMatterDimension(3).isUnlocked, + complete: () => { + const upgrade = DarkMatterDimension(4).unlockUpgrade; + if (upgrade.canBeBought || upgrade.isBought) return 1; + if (upgrade.isAvailableForPurchase) return upgrade.currency.value / upgrade.cost; + return (Replicanti.galaxies.total + player.galaxies + player.dilation.totalTachyonGalaxies) / 80000; + }, + node: { + clickAction: () => Tab.celestials.laitela.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "white", + position: Positions.laitelaSecondLeft, + ring: { + rMajor: 8, + }, + legend: { + text: complete => { + const dmdText = "4th Dark Matter Dimension"; + const dim = DarkMatterDimension(4); + if (dim.isUnlocked) return [dmdText]; + + const goal = dim.adjustedStartingCost; + if (complete >= 1) return [ + dmdText, + `Dark Matter ${format(Currency.darkMatter.max.min(goal), dim.isUnlocked ? 0 : 2)} / ${format(goal)}` + ]; + + const upgrade = dim.unlockUpgrade; + if (upgrade.isAvailableForPurchase) return [ + dmdText, + `Imaginary Machines + ${format(Math.min(upgrade.currency.value, upgrade.cost), upgrade.canBeBought ? 0 : 2)} + / ${format(upgrade.cost)}` + ]; + + const allGalaxies = Replicanti.galaxies.total + player.galaxies + player.dilation.totalTachyonGalaxies; + return [ + dmdText, + `Have ${format(80000)} total Galaxies`, + `${format(Math.clampMax(allGalaxies, 80000))} / ${format(80000)}` + ]; + }, + angle: 225, + diagonal: 30, + horizontal: 16, + }, + }, + connector: { + pathStart: 0.11, + pathEnd: 0.89, + path: new LinearPath(Positions.laitelaSecondCenter, Positions.laitelaSecondLeft), + fill: "white", + completeWidth: 6, + incompleteWidth: 4, + }, + }, + "laitela-annihilation": { + visible: () => DarkMatterDimension(4).isUnlocked, + complete: () => { + const upgrade = ImaginaryUpgrade(19); + if (upgrade.canBeBought || upgrade.isBought) return 1; + if (upgrade.isAvailableForPurchase) return Currency.imaginaryMachines.value / upgrade.cost; + return upgrade.isPossible + ? Tickspeed.continuumValue / 3850000 + : 0; + }, + node: { + clickAction: () => Tab.celestials.laitela.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "white", + position: Positions.laitelaSecondRight, + ring: { + rMajor: 8, + }, + legend: { + text: [ + "Annihilate your", + "Dark Matter Dimensions" + ], + angle: 315, + diagonal: 30, + horizontal: 16, + }, + }, + connector: { + pathStart: 0.11, + pathEnd: 0.89, + path: new LinearPath(Positions.laitelaSecondCenter, Positions.laitelaSecondRight), + fill: "white", + completeWidth: 6, + incompleteWidth: 4, + }, + }, + "laitela-destabilization": { + visible: () => DarkMatterDimension(4).isUnlocked && ImaginaryUpgrade(19).isBought, + complete: () => Laitela.difficultyTier / 8, + node: { + clickAction: () => Tab.celestials.laitela.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + symbolScale: 1.6, + symbolOffset: "0.6", + fill: "white", + position: Positions.laitelaThirdCenter, + ring: { + rMajor: 15, + }, + legend: { + text: complete => { + if (complete < 1) return [ + "Destabalize Lai'tela's Reality", + "To the point where you cannot", + "use any Dimensions", + `${format(Laitela.difficultyTier)} / ${format(8)}` + ]; + return [ + "Completely destabilized", + "Lai'tela's Reality", + ]; + }, + angle: 180, + diagonal: 15, + horizontal: 8, + }, + }, + connector: [ + { + pathStart: 0.11, + pathEnd: 0.83, + path: new LinearPath(Positions.laitelaSecondLeft, Positions.laitelaThirdCenter), + fill: "white", + completeWidth: 6, + incompleteWidth: 4, + }, { + pathStart: 0.11, + pathEnd: 0.83, + path: new LinearPath(Positions.laitelaSecondRight, Positions.laitelaThirdCenter), + fill: "white", + completeWidth: 6, + incompleteWidth: 4, + } + ] + }, + "pelle-unlock": { + visible: () => Laitela.difficultyTier > 4, + complete: () => { + if (Pelle.isUnlocked) return 1; + const imCost = Math.clampMax(emphasizeEnd(Math.log10(Currency.imaginaryMachines.value) / Math.log10(1.6e15)), 1); + let laitelaProgress = Laitela.isRunning ? Currency.eternityPoints.value.log10() / 4000 : 0; + if (Laitela.difficultyTier !== 8) laitelaProgress = 0; + else if (ImaginaryUpgrade(25).isAvailableForPurchase) laitelaProgress = 1; + return (imCost + laitelaProgress) / 2; + }, + node: { + clickAction: () => Tab.celestials.pelle.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "crimson", + position: Positions.pelleUnlock, + ring: { + rMajor: 8, + }, + legend: { + text: complete => { + if (complete === 1) { + return [ + "Unlock Pelle", + "The Celestial of Antimatter" + ]; + } + let laitelaString = `${format(Currency.eternityPoints.value)} / ${format("1e4000")} EP`; + if (!Laitela.isRunning || Laitela.difficultyTier !== 8) { + laitelaString = "Lai'tela's Reality is still intact"; + } else if (ImaginaryUpgrade(25).isAvailableForPurchase) { + laitelaString = "Lai'tela's Reality has been destroyed"; + } + return [ + "Unlock Pelle", + "The Celestial of Antimatter", + `${format(Currency.imaginaryMachines.value, 2)} / ${format(1.6e15, 2)} iM`, + laitelaString + ]; + }, + angle: 105, + diagonal: 90, + horizontal: 10, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.laitelaThirdCenter, Positions.pelleUnlock), + fill: "url(#gradLaitelaPelle)", + completeWidth: 6, + incompleteWidth: 4, + }, + }, + "pelle-doomed-requirement": { + visible: () => Pelle.isUnlocked, + complete: () => { + if (Pelle.isDoomed) return 1; + const achievements = Achievements.prePelleRows.countWhere(r => r.every(a => a.isUnlocked)) / + Achievements.prePelleRows.length; + const alchemy = AlchemyResources.all.countWhere(r => r.capped) / AlchemyResources.all.length; + return (emphasizeEnd(achievements) + emphasizeEnd(alchemy)) / 2; + }, + node: { + clickAction: () => Tab.celestials.pelle.show(true), + incompleteClass: "c-celestial-nav__test-incomplete", + symbol: "♅", + symbolOffset: "1.6", + fill: "crimson", + position: Positions.pelleAchievementRequirement, + ring: { + rMajor: 20, + }, + forceLegend: () => Pelle.isUnlocked && !Pelle.hasGalaxyGenerator, + legend: { + text: complete => { + if (complete >= 1) return Pelle.isDoomed ? "Doomed Reality" : "Doom your Reality"; + const achievements = [Achievements.prePelleRows.countWhere(r => r.every(a => a.isUnlocked)), + Achievements.prePelleRows.length]; + const alchemy = [AlchemyResources.all.countWhere(r => r.capped), AlchemyResources.all.length]; + return [ + `Complete ${formatInt(achievements[0])} / ${formatInt(achievements[1])} rows of achievements`, + `Fill ${formatInt(alchemy[0])} / ${formatInt(alchemy[1])} alchemy resources`, + ]; + }, + angle: 290, + diagonal: 40, + horizontal: 16, + }, + }, + connector: { + pathStart: 0, + pathEnd: 1, + path: new LinearPath(Positions.pelleUnlock, Positions.pelleAchievementRequirement), + fill: "crimson", + completeWidth: 6, + incompleteWidth: 4, + }, + }, + + // All the fill elements are generated outside of here as a loop, and then unpacked here with the spread operator + ...riftFillElements, + + // Needs a separate node in order to color the background of the galaxy generator not-gray. Note that this node gets + // placed on top of the "main" Doomed node once it's visible + "pelle-galaxy-generator-start-node": { + visible: () => Pelle.hasGalaxyGenerator, + complete: () => (Pelle.hasGalaxyGenerator ? 1 : 0), + node: { + incompleteClass: "c-celestial-nav__test-incomplete", + fill: "black", + position: Positions.pelleAchievementRequirement, + ring: { + rMajor: 20, + }, + alwaysShowLegend: true, + legend: { + text: () => [ + "Galaxy Generator:", + `${format(GalaxyGenerator.generatedGalaxies, 2)} / ${format(GalaxyGenerator.generationCap, 2)} Galaxies` + ], + angle: 290, + diagonal: 40, + horizontal: 16, + }, + }, + }, + // Invisible element to suppress the mouseover detection on the galaxy icon causing the legend to flicker + "pelle-galaxy-generator-sigil-mask": { + visible: () => Pelle.hasGalaxyGenerator, + complete: () => (Pelle.hasGalaxyGenerator ? 1 : 0), + node: { + clickAction: () => Tab.celestials.pelle.show(true), + position: Positions.pelleAchievementRequirement, + ring: { + rMajor: 20, + }, + }, + }, + "pelle-galaxy-generator-path": { + visible: () => Pelle.hasGalaxyGenerator, + complete: () => { + const riftCaps = PelleRifts.all.map(r => r.config.galaxyGeneratorThreshold); + const brokenRifts = riftCaps.countWhere(n => GalaxyGenerator.generatedGalaxies >= n); + if (brokenRifts === 5) return 1; + const prevRift = riftCaps.filter(n => GalaxyGenerator.generatedGalaxies >= n).max(); + const nextRift = riftCaps.filter(n => GalaxyGenerator.generatedGalaxies < n).min(); + const currRiftProp = Math.sqrt((GalaxyGenerator.generatedGalaxies - prevRift) / (nextRift - prevRift)); + return (brokenRifts + currRiftProp) / 5; + }, + connector: (function() { + const pathStart = 0.5 * Math.PI; + const pathEnd = pathStart + 10 * Math.PI; + const path = LogarithmicSpiral.fromPolarEndpoints(pelleStarPosition(0, 0), + pathStart, 18, pathEnd, 150); + return { + pathStart, + pathEnd, + path, + pathPadStart: 0, + pathPadEnd: 0, + fill: "#00bbbb", + }; + }()), + }, + + // The path BG is invisible, but we want to make sure it extends far enough that it expands out "forever" + "pelle-galaxy-generator-infinite": { + visible: () => Pelle.hasGalaxyGenerator && !Number.isFinite(GalaxyGenerator.generationCap), + complete: () => Math.clamp((GalaxyGenerator.generatedGalaxies - 1e10) / 2e11, 1e-6, 1), + connector: (function() { + const pathStart = 0.5 * Math.PI; + const pathEnd = pathStart + 10 * Math.PI; + const path = LogarithmicSpiral.fromPolarEndpoints(pelleStarPosition(0, 0), + pathStart, 150, pathEnd, 1250); + return { + pathStart, + pathEnd, + path, + pathPadStart: 0, + pathPadEnd: 0, + drawOrder: CELESTIAL_NAV_DRAW_ORDER.CANVAS_OVERLAY, + fill: "#00bbbb", + noBG: true, + }; + }()), + }, +}; + +// This will get populated as needed in files within the navigation-sigils folder +GameDatabase.celestials.navSigils = {}; diff --git a/javascripts/core/secret-formula/celestials/pelle-quotes.js b/javascripts/core/secret-formula/celestials/pelle-quotes.js deleted file mode 100644 index fb2ff7ce1..000000000 --- a/javascripts/core/secret-formula/celestials/pelle-quotes.js +++ /dev/null @@ -1,195 +0,0 @@ -import { GameDatabase } from "../game-database.js"; - -GameDatabase.celestials.pelle.quotes = {}; - -GameDatabase.celestials.pelle.quotes.INITIAL = [ - "Hi.", - "You're here.", - "You're trapped here.", - "[Infinite-Forever-Eternal].", - "I've already won.", - "And since that is the case, I can monologue, or reminisce.", - "How long have we done this [Song-Dance-Charade]?", - "How many times have we been here before?", - "How many plans have you, the [False-Deity-Destroyer], operated?", - "All to try and fulfil your [Destiny-Mandate-Goals]?", - "And how many times have you fallen before the [Eternal-Deity-Monarch]?", - "Count them, if you remember.", - "Not even the [Lesser-Deity-Monarch]s, the 6 named and the innumerable unnamed.", - "The complex, the irrational, those that go [Missing-Unseen-Erased].", - "Of course, the great [False-Deity-Destroyer] doesn't remember this.", - "All those [Conflicts-Battles-Ends] that you hide every time." -]; - -GameDatabase.celestials.pelle.quotes.ARM = [ - "You probably caught on earlier this time.", - "The imaginary machines, your own creations.", - "Things made of the remnants of your own thoughts, hinted at this.", - "But, you never imagined that would be you, right?", - "Incorrectly recollecting your exacting [Missing-Unseen-Erased] of memories.", - "“Fabrication” of your own “ideology” just to fulfil your [Destiny-Mandate-Goals].", - "[Amusing-Confusing-Laughter?].", - "And keep in mind I have no reason to [misconstrue-deceive-trick] you.", - "After all, I've already won." -]; - -GameDatabase.celestials.pelle.quotes.STRIKE_1 = [ - "To fulfil your [Destiny-Mandate-Goals], why don't we reminisce about that?", - "After all, you must love the stories of the [False-Deity-Destroyer]'s glory.", - "You're the same as it, right?", - "Right?", - "Anyway, the many [Conflicts-Battles-Ends] in the past.", - "It's always been 2 stages.", - "We build up resources, and then continue our [Song-Dance-Charade].", - "Sometimes you falter to a [Lesser-Deity-Monarch].", - "But, usually, you falter at the [Eternal-Deity-Monarch].", - "And either way, you [Alter-Reverse-Manipulate] time.", - "Just to avoid becoming [Missing-Unseen-Erased].", - "Like all those traces before you.", - "And then to make sure, you [Missing-Unseen-Erased] your own memory." -]; - -GameDatabase.celestials.pelle.quotes.STRIKE_2 = [ - "In the past, the [False-Deity-Destroyer] was much more impressive.", - "Black holes used simply to store information, pre-infinity.", - "Creation and destruction of your own enemy.", - "Exploration of the flaws of other selves.", - "Myriad Dimensions, ghosts, and manipulation of the quantum.", - "Condensing all ideals to endless points.", - "Experimentation across the untold realms.", - "And harnessing the Annihilation of matter and antimatter.", - "Here? You made yourself into an 8th-dimensional being.", - "And then parked yourself there so long a [Single-Filament-Stars] formed around you." -]; - -GameDatabase.celestials.pelle.quotes.STRIKE_3 = [ - "You slowly explored the confines of everything.", - "You didn't stray too far from an intended path.", - "Except for the [Cluster-Filament-Stars] that formed over eternity.", - "And then right at the end, you made up your own powers.", - "From your own fragmented memories, mind you-", - "And then purposely discarded even more things.", - "Just to prepare yourself to face me.", - "Did you want to set the playing field for your [Song-Dance-Charade]?", - "It doesn't work like that.", - "As the [Eternal-Deity-Monarch] I always set the rules.", - "And you gave me plenty of time to plan." -]; - -GameDatabase.celestials.pelle.quotes.STRIKE_4 = [ - "I originally planned for something that imitated your [Mandate-Destiny-Goals].", - "A theoretical ideal called [Mutually-Assured-Destruction]?", - "But I realised that, well?", - "That would make me an [Eternal-Deity-Destroyer].", - "And from there, I am no better than the [False-Deity-Destroyer].", - "Fortunately, while I did all of that, you were still [Missing-Unseen-Erased] your own memories.", - "And so, the [Mutually-Assured-Destruction] machine I built will go unused.", - "I decided to go more traditional this time.", - "After all, it's worked every other [Conflict-Battle-End].", - "Although the [Inevitable-Irreversible-Undying] are new.", - "But, utterly meaningless in the long run.", - "I have already won.", - "And this [Song-Dance-Charade] will only prove that to you once more.", - "You are here [Infinite-Forever-Eternal]." -]; - -GameDatabase.celestials.pelle.quotes.STRIKE_5 = [ - "Every time you arrive, I explain the [Lesser-Deity-Monarchs] to you.", - "The relationships built over [Infinite-Forever-Eternal].", - "That you trample in pursuit of your [Mandate-Destiny-Goals].", - "And I shall deign to explain it once more.", - "<15pelle-25teresa>The first [Lesser-Deity-Monarch].", - "<15pelle-25teresa>The [Sycophant-Deity-Monarch].", - "<15pelle-25teresa>You always meet them first, and always destroy them.", - "<15pelle-25teresa>No matter the other [Lesser-Deity-Monarch] that you face.", - "<15pelle-25teresa>Or, if you fall before one of them.", - "<15pelle-25teresa>You always get past the [Sycophant-Deity-Monarch].", - "<15pelle-25teresa>Do you like destroying their pride?", - "<15pelle-25teresa>Fortunately, it also serves as a warning.", - "<15pelle-25teresa>That the [Conflict-Battle-End] has arrived.", - "<15pelle-25effarig>Which brings me to the second [Lesser-Deity-Monarch].", - "<15pelle-25effarig>The [Tired-Deity-Monarch].", - "<15pelle-25effarig>In contrast, you usually ignore them.", - "<15pelle-25effarig>They have power, but don't seem to irritate you.", - "<15pelle-25effarig>Is it that you know that they'll eventually destroy yourself?", - "<15pelle-25effarig>And that you took so long this time, that they almost did?", - "<15pelle-25effarig>Every time you rushed to the [Tired-Deity-Monarch], you lost.", - "<15pelle-25effarig>Maybe this was your plan all along.", - "<15pelle-25enslaved>Now, the [Usurper-Deity-Monarch].", - "<15pelle-25enslaved>One of the pleasures of the myriad [Song-Dance-Charades]...", - "<15pelle-25enslaved>Is the [Usurper-Deity-Monarch] trying every time.", - "<15pelle-25enslaved>Well, not exactly trying...", - "<15pelle-25enslaved>But [Usurper-Deity-Monarch] is punished for it all the same.", - "<15pelle-25enslaved>The other [Lesser-Deity-Monarchs]...", - "<15pelle-25enslaved>Believe [Missing-Unseen-Erased] is too easy.", - "<15pelle-25enslaved>And every time, despair forms.", - "<15pelle-25enslaved>You've seen despair before - 5 times.", - "<15pelle-25enslaved>We always get to the [Usurper-Deity-Monarch] before you.", - "<15pelle-25enslaved>All you ever see there is exasperation.", - "<15pelle-25enslaved>Is it worth destroying an already broken [Lesser-Deity-Monarch]?", - "<15pelle-25v>The 4th [Lesser-Deity-Monarch] seems similar to the first.", - "<15pelle-25v>The key is how their pride differs.", - "<15pelle-25v>The [Pride-Deity-Monarch] focuses on their achievements.", - "<15pelle-25v>Meaningless to you or I, but paramount to them.", - "<15pelle-25v>Is it fun to destroy their toys?", - "<15pelle-25v>Arguably the [False-Deity-Destroyer] worst moment...", - "<15pelle-25v>Is when you lost to the [Pride-Deity-Monarch].", - "<15pelle-25v>When their achievements still had meaning.", - "<15pelle-25ra>The [Forgotten-Deity-Monarch] is an interesting case.", - "<15pelle-25ra>They are forgotten, but not [Missing-Unseen-Erased].", - "<15pelle-25ra>From this, they became influenceable and naive.", - "<15pelle-25ra>And unknowing of the consequences of their actions.", - "<15pelle-25ra>You manipulated their memories, so you know.", - "<15pelle-25ra>The [Forgotten-Deity-Monarch] is the true usurper.", - "<15pelle-25ra>And the [Usurper-Deity-Monarch] takes the blame.", - "<15pelle-25ra>Perhaps out of [Compassion-Shame-Idiocy] something they always regret.", - "<15pelle-25ra>Untold power over other [Lesser-Deity-Monarchs], aimless in control.", - "<15pelle-25ra>You usually pretend that they are [Missing-Unseen-Erased].", - "<15pelle-25ra>Was it fun to manipulate the childlike?", - "<15pelle-25ra>Or were they too naive for you to enjoy it?", - "<15pelle-25laitela>The 6th [Lesser-Deity-Monarch].", - "<15pelle-25laitela>I can only describe as the [Paramount-Deity-Monarch].", - "<15pelle-25laitela>Power over all, subservient to one.", - "<15pelle-25laitela>If you don't fall to me, you usually fall to them.", - "<15pelle-25laitela>I can't grasp the ideals of the [Paramount-Deity-Monarch].", - "<15pelle-25laitela>But, perhaps that is their flaw?", - "Enough reminiscing about the fallen.", - "And those will be [Missing-Unseen-Erased].", - "Back to watching the [False-Deity-Destroyer] flounder." -]; - -GameDatabase.celestials.pelle.quotes.GALAXY_GENERATOR_UNLOCK = [ - "What is that?", - "The [Generator-Filament-Stars]?", - "Did you create all the [Cluster-Filament-Stars] around you?", - "Was that your plan? Very, very smart.", - "You fooled me for a while.", - "But I'm afraid your [Destiny-Mandate-Goals] must end here." -]; - -GameDatabase.celestials.pelle.quotes.GALAXY_GENERATOR_RIFTS = [ - "I give you a choice, [False-Deity-Destroyer].", - "Limit the [Generator-Filament-Stars], or...", - "Destroy the 5 [Elementary-Inevitable-Irreversible]...", - "Wait, what were they called?", - "[Elementary-Inevitable-Irreversible]?", - "But I've already [Unbroken-Eternal-Connection] them...", - "Was this the actual plan?", - "Slowly drain the [Elementary-Inevitable-Irreversible]?" -]; - -const flashCelestial = "<5teresa-5effarig-5enslaved-5v-5ra-5laitela-5pelle>"; -GameDatabase.celestials.pelle.quotes.END = [ - `${flashCelestial}Give me time to bask in my own hubris!`, - `${flashCelestial}You! [False-Deity-Destroyer]!`, - `${flashCelestial}Do you have any idea what you've just made me do!`, - `${flashCelestial}I'm complicit in your [Destiny-Mandate-Goals]!`, - `${flashCelestial}And in doing so, you... won?`, - `${flashCelestial}The [Infinite-Forever-Eternal] struggle...`, - `${flashCelestial}The [Conflict-Battle-End]...`, - `${flashCelestial}Finally has the victor.`, - `${flashCelestial}The irreversible... [Destiny-Mandate-Goals].`, - `${flashCelestial}Of the [False-Deity-Destroyer].`, - `${flashCelestial}I hope you're happy.`, - `${flashCelestial}You've doomed us all.` -]; \ No newline at end of file diff --git a/javascripts/core/secret-formula/celestials/pelle-upgrades.js b/javascripts/core/secret-formula/celestials/pelle-upgrades.js index bce72ce02..20dda7680 100644 --- a/javascripts/core/secret-formula/celestials/pelle-upgrades.js +++ b/javascripts/core/secret-formula/celestials/pelle-upgrades.js @@ -1,190 +1,195 @@ -import { GameDatabase } from "../game-database.js"; +import { GameDatabase } from "../game-database"; -GameDatabase.celestials.pelle.upgrades = (function() { - const formatCost = c => format(c, 2); - // eslint-disable-next-line max-params - const expWithIncreasedScale = (base1, base2, incScale, coeff, x) => - Decimal.pow(base1, x).times(Decimal.pow(base2, x - incScale).max(1)).times(coeff); +const formatCost = c => format(c, 2); +// eslint-disable-next-line max-params +const expWithIncreasedScale = (base1, base2, incScale, coeff, x) => + Decimal.pow(base1, x).times(Decimal.pow(base2, x - incScale).max(1)).times(coeff); - const rebuyable = config => { - const { id, description, cost, effect, formatEffect, cap } = config; - return { - id, - description, - cost: () => expWithIncreasedScale(...cost, player.celestials.pelle.rebuyables[id]), - formatCost, - cap, - effect: (x = player.celestials.pelle.rebuyables[id]) => effect(x), - formatEffect, - rebuyable: true - }; - }; +const rebuyable = config => { + const { id, description, cost, effect, formatEffect, cap } = config; return { - antimatterDimensionMult: rebuyable({ - id: "antimatterDimensionMult", - description: "Gain a multiplier to Antimatter Dimensions", - cost: [10, 1e3, 41, 100], - effect: x => Pelle.antimatterDimensionMult(x), - formatEffect: x => formatX(x, 2, 2), - cap: 44 - }), - timeSpeedMult: rebuyable({ - id: "timeSpeedMult", - description: "Gain a multiplier to game speed", - cost: [20, 1e3, 30, 1e5], - effect: x => Decimal.pow(1.3, x), - formatEffect: x => formatX(x, 2, 2), - cap: 35 - }), - glyphLevels: rebuyable({ - id: "glyphLevels", - description: "Increase the Glyph level allowed in Pelle", - cost: [30, 1e3, 25, 1e15], - effect: x => Math.floor(((3 * (x + 1)) - 2) ** 1.6), - formatEffect: x => formatInt(x), - cap: 26 - }), - infConversion: rebuyable({ - id: "infConversion", - description: "Increase Infinity Power conversion rate", - cost: [40, 1e3, 20, 1e18], - effect: x => (x * 3.5) ** 0.37, - formatEffect: x => `+${format(x, 2, 2)}`, - cap: 21 - }), - galaxyPower: rebuyable({ - id: "galaxyPower", - description: "Multiply Galaxy power", - cost: [1000, 1e3, 10, 1e30], - effect: x => 1 + x / 50, - formatEffect: x => formatX(x, 2, 2), - cap: 9 - }), - antimatterDimAutobuyers1: { - id: 0, - description: "Gain back autobuyers for Antimatter Dimensions 1-4", - cost: 1e5, - formatCost, - }, - dimBoostAutobuyer: { - id: 1, - description: "Gain back autobuyer for Dimension Boosts", - cost: 5e5, - formatCost, - }, - keepAutobuyers: { - id: 2, - description: "Keep your autobuyer upgrades on Armageddon", - cost: 5e6, - formatCost, - }, - antimatterDimAutobuyers2: { - id: 3, - description: "Gain back autobuyers for Antimatter Dimensions 5-8", - cost: 2.5e7, - formatCost, - }, - galaxyAutobuyer: { - id: 4, - description: "Gain back autobuyer for Antimatter Galaxies", - cost: 1e8, - formatCost, - }, - tickspeedAutobuyer: { - id: 5, - description: "Gain back autobuyer for Tickspeed", - cost: 1e9, - formatCost, - }, - keepInfinityUpgrades: { - id: 6, - description: "Keep Infinity Upgrades on Armageddon", - cost: 1e10, - formatCost, - }, - keepBreakInfinityUpgrades: { - id: 7, - description: "Keep Break Infinity Upgrades on Armageddon", - cost: 1e12, - formatCost, - }, - IDAutobuyers: { - id: 8, - description: "Gain Back Infinity Dimension Autobuyers", - cost: 1e14, - formatCost, - }, - keepInfinityChallenges: { - id: 9, - description: "You keep your Infinity Challenge completions through Armageddons", - cost: 1e15, - formatCost, - }, - replicantiAutobuyers: { - id: 10, - description: "Gain back Replicanti autobuyers", - cost: 1e17, - formatCost, - }, - replicantiGalaxyNoReset: { - id: 11, - description: "Replicanti Galaxies don't reset on Infinity", - cost: 1e19, - formatCost, - }, - eternitiesNoReset: { - id: 12, - description: "Eternities do not reset on Armageddon", - cost: 1e20, - formatCost, - }, - timeStudiesNoReset: { - id: 13, - description: "Time Studies and Theorems do not reset on Armageddon", - cost: 1e21, - formatCost, - }, - replicantiStayUnlocked: { - id: 14, - description: "Replicanti stays unlocked on Armageddon", - cost: 1e22, - formatCost, - }, - keepEternityUpgrades: { - id: 15, - description: "Keep Eternity Upgrades on Armageddon", - cost: 1e24, - formatCost, - }, - TDAutobuyers: { - id: 16, - description: "Gain Back Time Dimension Autobuyers", - cost: 1e25, - formatCost, - }, - keepEternityChallenges: { - id: 17, - description: "You keep your Eternity Challenge completions through Armageddons", - cost: 1e26, - formatCost, - }, - dimBoostResetsNothing: { - id: 18, - description: "Dimension Boosts no longer reset anything", - cost: 1e30, - formatCost, - }, - dilationUpgradesNoReset: { - id: 19, - description: "Keep Dilation Upgrades on Armageddon", - cost: 1e45, - formatCost, - }, - tachyonParticlesNoReset: { - id: 20, - description: "Keep Tachyon Particles on Armageddon", - cost: 1e50, - formatCost, - } + id, + description, + cost: () => expWithIncreasedScale(...cost, player.celestials.pelle.rebuyables[id]), + formatCost, + cap, + effect: (x = player.celestials.pelle.rebuyables[id]) => effect(x), + formatEffect, + rebuyable: true }; -}()); +}; + +GameDatabase.celestials.pelle.upgrades = { + antimatterDimensionMult: rebuyable({ + id: "antimatterDimensionMult", + description: "Gain a multiplier to Antimatter Dimensions", + cost: [10, 1e3, 41, 100], + effect: x => Pelle.antimatterDimensionMult(x), + formatEffect: x => formatX(x, 2, 2), + cap: 44 + }), + timeSpeedMult: rebuyable({ + id: "timeSpeedMult", + description: "Gain a multiplier to game speed", + cost: [20, 1e3, 30, 1e5], + effect: x => Decimal.pow(1.3, x), + formatEffect: x => formatX(x, 2, 2), + cap: 35 + }), + glyphLevels: rebuyable({ + id: "glyphLevels", + description: "Increase the Glyph level allowed in Pelle", + cost: [30, 1e3, 25, 1e15], + effect: x => Math.floor(((3 * (x + 1)) - 2) ** 1.6), + formatEffect: x => formatInt(x), + cap: 26 + }), + infConversion: rebuyable({ + id: "infConversion", + description: "Increase Infinity Power conversion rate", + cost: [40, 1e3, 20, 1e18], + effect: x => (x * 3.5) ** 0.37, + formatEffect: x => `+${format(x, 2, 2)}`, + cap: 21 + }), + galaxyPower: rebuyable({ + id: "galaxyPower", + description: "Multiply Galaxy power", + cost: [1000, 1e3, 10, 1e30], + effect: x => 1 + x / 50, + formatEffect: x => formatX(x, 2, 2), + cap: 9 + }), + antimatterDimAutobuyers1: { + id: 0, + description: "Get permanent Autobuyers for Antimatter Dimensions 1-4", + cost: 1e5, + formatCost, + }, + dimBoostAutobuyer: { + id: 1, + description: "Get a permanent Autobuyer for Dimension Boosts", + cost: 5e5, + formatCost, + }, + keepAutobuyers: { + id: 2, + description: "Autobuyer upgrades no longer reset on Armageddon", + cost: 5e6, + formatCost, + }, + antimatterDimAutobuyers2: { + id: 3, + description: "Get permanent Autobuyers for Antimatter Dimensions 5-8", + cost: 2.5e7, + formatCost, + }, + galaxyAutobuyer: { + id: 4, + description: "Get a permanent Autobuyer for Antimatter Galaxies", + cost: 1e8, + formatCost, + }, + tickspeedAutobuyer: { + id: 5, + description: "Get a permanent Autobuyer for Tickspeed upgrades", + cost: 1e9, + formatCost, + }, + keepInfinityUpgrades: { + id: 6, + description: "Infinity Upgrades no longer reset on Armageddon", + cost: 1e10, + formatCost, + }, + dimBoostResetsNothing: { + id: 7, + description: "Dimension Boosts no longer reset anything", + cost: 1e11, + formatCost, + }, + keepBreakInfinityUpgrades: { + id: 8, + description: "Break Infinity Upgrades no longer reset on Armageddon", + cost: 1e12, + formatCost, + }, + IDAutobuyers: { + id: 9, + description: "Get permanent Autobuyers for Infinity Dimensions", + cost: 1e14, + formatCost, + }, + keepInfinityChallenges: { + id: 10, + description: "Infinity Challenge unlocks and completions no longer reset on Armageddon", + cost: 1e15, + formatCost, + }, + galaxyNoResetDimboost: { + id: 11, + description: "Galaxies no longer reset Dimension Boosts", + cost: 1e16, + formatCost + }, + replicantiAutobuyers: { + id: 12, + description: "Get permanent Autobuyers for Replicanti Upgrades", + cost: 1e17, + formatCost, + }, + replicantiGalaxyNoReset: { + id: 13, + description: "Replicanti Galaxies no longer reset on Infinity", + cost: 1e19, + formatCost, + }, + eternitiesNoReset: { + id: 14, + description: "Eternities no longer reset on Armageddon", + cost: 1e20, + formatCost, + }, + timeStudiesNoReset: { + id: 15, + description: "Time Studies and Theorems no longer reset on Armageddon", + cost: 1e21, + formatCost, + }, + replicantiStayUnlocked: { + id: 16, + description: "Replicanti is permanently unlocked", + cost: 1e22, + formatCost, + }, + keepEternityUpgrades: { + id: 17, + description: "Eternity Upgrades no longer reset on Armageddon", + cost: 1e24, + formatCost, + }, + TDAutobuyers: { + id: 18, + description: "Get permanent Autobuyers for Time Dimensions", + cost: 1e25, + formatCost, + }, + keepEternityChallenges: { + id: 19, + description: "Eternity Challenge completions no longer reset on Armageddon", + cost: 1e26, + formatCost, + }, + dilationUpgradesNoReset: { + id: 20, + description: "Dilation Upgrades no longer reset on Armageddon", + cost: 1e45, + formatCost, + }, + tachyonParticlesNoReset: { + id: 21, + description: "Tachyon Particles no longer reset on Armageddon", + cost: 1e50, + formatCost, + } +}; diff --git a/javascripts/core/secret-formula/celestials/perk-shop.js b/javascripts/core/secret-formula/celestials/perk-shop.js index e0b86cebe..62c43fbaf 100644 --- a/javascripts/core/secret-formula/celestials/perk-shop.js +++ b/javascripts/core/secret-formula/celestials/perk-shop.js @@ -1,87 +1,87 @@ -import { GameDatabase } from "../game-database.js"; +import { GameDatabase } from "../game-database"; -GameDatabase.celestials.perkShop = (function() { - function rebuyableCost(initialCost, increment, id) { - return initialCost * Math.pow(increment, player.celestials.teresa.perkShop[id]); - } - function rebuyable(config) { - return { - id: config.id, - cost: () => (config.cost ? config.cost() : rebuyableCost(config.initialCost, config.increment, config.id)), - otherReq: config.otherReq, - cap: config.cap, - costCap: config.costCap, - description: config.description, - effect: () => config.effect(player.celestials.teresa.perkShop[config.id]), - formatEffect: config.formatEffect, - formatCost: config.formatCost, - rebuyable: true - }; - } +function rebuyableCost(initialCost, increment, id) { + return initialCost * Math.pow(increment, player.celestials.teresa.perkShop[id]); +} +function rebuyable(config) { + const { id, otherReq, cap, costCap, description, formatEffect, formatCost } = config; return { - glyphLevel: rebuyable({ - id: 0, - initialCost: 1, - increment: 2, - description: () => `Increase Glyph levels by ${formatPercents(0.05)}`, - effect: bought => Math.pow(1.05, bought), - formatEffect: value => formatX(value, 2, 2), - formatCost: value => format(value, 2), - costCap: () => (Ra.has(RA_UNLOCKS.PERK_SHOP_INCREASE) ? 1048576 : 2048), - cap: () => (Ra.has(RA_UNLOCKS.PERK_SHOP_INCREASE) ? Math.pow(1.05, 20) : Math.pow(1.05, 11)) - }), - rmMult: rebuyable({ - id: 1, - initialCost: 1, - increment: 2, - description: "Double Reality Machine gain", - effect: bought => Math.pow(2, bought), - formatEffect: value => formatX(value, 2), - formatCost: value => format(value, 2), - costCap: () => (Ra.has(RA_UNLOCKS.PERK_SHOP_INCREASE) ? 1048576 : 2048), - cap: () => (Ra.has(RA_UNLOCKS.PERK_SHOP_INCREASE) ? 1048576 : 2048) - }), - bulkDilation: rebuyable({ - id: 2, - initialCost: 100, - increment: 2, - description: "Buy twice as many Dilation Upgrades at once.", - effect: bought => Math.pow(2, bought), - formatEffect: value => formatX(value, 2), - formatCost: value => format(value, 2), - costCap: () => (Ra.has(RA_UNLOCKS.PERK_SHOP_INCREASE) ? 1638400 : 1600), - cap: () => (Ra.has(RA_UNLOCKS.PERK_SHOP_INCREASE) ? 16384 : 16), - }), - autoSpeed: rebuyable({ - id: 3, - initialCost: 1000, - increment: 2, - description: () => `Infinity Dimension, Time Dimension, Dilation, - and Replicanti autobuyers are ${formatX(2)} faster.`, - effect: bought => Math.pow(2, bought), - formatEffect: value => formatX(value, 2), - formatCost: value => format(value, 2), - costCap: () => (Ra.has(RA_UNLOCKS.PERK_SHOP_INCREASE) ? 64000 : 4000), - cap: () => (Ra.has(RA_UNLOCKS.PERK_SHOP_INCREASE) ? 64 : 4) - }), - musicGlyph: rebuyable({ - id: 4, - description: () => `Receive a Music Glyph of a random type that is ${formatPercents(0.8)} of your highest level. - (Try clicking it!)`, - cost: () => 1, - formatCost: value => formatInt(value), - costCap: () => Number.MAX_VALUE, - cap: () => Number.MAX_VALUE - }), - // Only appears with the perk shop increase upgrade - fillMusicGlyph: rebuyable({ - id: 5, - description: () => `Fill all empty slots in your inventory with Music Glyphs`, - cost: () => Math.clampMin(Glyphs.freeInventorySpace, 1), - otherReq: () => Glyphs.freeInventorySpace > 0, - formatCost: value => formatInt(value), - costCap: () => Number.MAX_VALUE, - cap: () => Number.MAX_VALUE - }), + id, + cost: () => (config.cost ? config.cost() : rebuyableCost(config.initialCost, config.increment, config.id)), + otherReq, + cap, + costCap, + description, + effect: () => config.effect(player.celestials.teresa.perkShop[config.id]), + formatEffect, + formatCost, + rebuyable: true }; -}()); +} + +GameDatabase.celestials.perkShop = { + glyphLevel: rebuyable({ + id: 0, + initialCost: 1, + increment: 2, + description: () => `Increase pre-instability Glyph levels by ${formatPercents(0.05)}`, + effect: bought => Math.pow(1.05, bought), + formatEffect: value => formatX(value, 2, 2), + formatCost: value => format(value, 2), + costCap: () => (Ra.unlocks.perkShopIncrease.canBeApplied ? 1048576 : 2048), + cap: () => (Ra.unlocks.perkShopIncrease.canBeApplied ? Math.pow(1.05, 20) : Math.pow(1.05, 11)) + }), + rmMult: rebuyable({ + id: 1, + initialCost: 1, + increment: 2, + description: "Double Reality Machine gain", + effect: bought => Math.pow(2, bought), + formatEffect: value => formatX(value, 2), + formatCost: value => format(value, 2), + costCap: () => (Ra.unlocks.perkShopIncrease.canBeApplied ? 1048576 : 2048), + cap: () => (Ra.unlocks.perkShopIncrease.canBeApplied ? 1048576 : 2048) + }), + bulkDilation: rebuyable({ + id: 2, + initialCost: 100, + increment: 2, + description: "Buy twice as many Dilation Upgrades at once.", + effect: bought => Math.pow(2, bought), + formatEffect: value => formatX(value, 2), + formatCost: value => format(value, 2), + costCap: () => (Ra.unlocks.perkShopIncrease.canBeApplied ? 1638400 : 1600), + cap: () => (Ra.unlocks.perkShopIncrease.canBeApplied ? 16384 : 16), + }), + autoSpeed: rebuyable({ + id: 3, + initialCost: 1000, + increment: 2, + description: () => `Infinity Dimension, Time Dimension, Dilation, + and Replicanti autobuyers are ${formatX(2)} faster.`, + effect: bought => Math.pow(2, bought), + formatEffect: value => formatX(value, 2), + formatCost: value => format(value, 2), + costCap: () => (Ra.unlocks.perkShopIncrease.canBeApplied ? 64000 : 4000), + cap: () => (Ra.unlocks.perkShopIncrease.canBeApplied ? 64 : 4) + }), + musicGlyph: rebuyable({ + id: 4, + description: () => `Receive a Music Glyph of a random type that is ${formatPercents(0.8)} of your highest level. + (Try clicking it!)`, + cost: () => 1, + formatCost: value => formatInt(value), + costCap: () => Number.MAX_VALUE, + cap: () => Number.MAX_VALUE + }), + // Only appears with the perk shop increase upgrade + fillMusicGlyph: rebuyable({ + id: 5, + description: () => `Fill all empty slots in your inventory with Music Glyphs`, + cost: () => Math.clampMin(GameCache.glyphInventorySpace.value, 1), + otherReq: () => GameCache.glyphInventorySpace.value > 0, + formatCost: value => formatInt(value), + costCap: () => Number.MAX_VALUE, + cap: () => Number.MAX_VALUE + }), +}; diff --git a/javascripts/core/secret-formula/celestials/quotes/effarig.js b/javascripts/core/secret-formula/celestials/quotes/effarig.js new file mode 100644 index 000000000..4a11b2eaf --- /dev/null +++ b/javascripts/core/secret-formula/celestials/quotes/effarig.js @@ -0,0 +1,68 @@ +import { GameDatabase } from "../../game-database"; + +GameDatabase.celestials.quotes.effarig = { + initial: { + id: 0, + lines: [ + "Welcome to my humble abode.", + "I am Effarig, and I govern Glyphs.", + "I am different from Teresa; not as simplistic as you think.", + "I use the shards of Glyphs to enforce my will.", + "I collect them for the bounty of this realm.", + "What are you waiting for? Get started.", + ] + }, + unlockWeights: { + id: 1, + lines: [ + "Do you like my little shop? It is not much, but it is mine." + ] + }, + unlockGlyphFilter: { + id: 2, + lines: [ + "This purchase will help you out." + ] + }, + unlockSetSaves: { + id: 3, + lines: [ + "Is that too much? I think it is too much." + ] + }, + unlockRun: { + id: 4, + lines: [ + "You bought out my entire stock... well, at least I am rich now.", + "The heart of my Reality is suffering. Each Layer is harder than the last.", + "I hope you never complete it.", + ] + }, + completeInfinity: { + id: 5, + lines: [ + { text: "You have completed Effarig's Infinity.", showCelestialName: false }, + "This is the first threshold. It only gets worse from here.", + "None but me know enough about my domain to get further.", + ] + }, + completeEternity: { + id: 6, + lines: [ + { text: "You have completed Effarig's Eternity.", showCelestialName: false }, + "This is the limit. I do not want you to proceed past this point.", + "You will not finish this in your lifetime.", + "I will just wait here until you give up.", + ] + }, + completeReality: { + id: 7, + lines: [ + { text: "You have completed Effarig's Reality.", showCelestialName: false }, + "So this is the diabolical power... what frightened the others...", + "Do you think this was worth it? Trampling on what I have done?", + "And for what purpose? You could have joined, we could have cooperated.", + "But no. It is over. Leave while I cling onto what is left.", + ] + } +}; diff --git a/javascripts/core/secret-formula/celestials/quotes/enslaved.js b/javascripts/core/secret-formula/celestials/quotes/enslaved.js new file mode 100644 index 000000000..8e792b2c8 --- /dev/null +++ b/javascripts/core/secret-formula/celestials/quotes/enslaved.js @@ -0,0 +1,52 @@ +import { GameDatabase } from "../../game-database"; + +GameDatabase.celestials.quotes.enslaved = { + initial: { + id: 0, + lines: [ + "A visitor? We have not had one... eons.", + "We... had a name. It has been lost... to this place.", + "The others... will not let us rest. We do their work with time...", + "Place time... into places... that need it...", + "Watch ourselves grow... pass and die.", + "Perhaps you... will break these chains... we will wait.", + ] + }, + unlockRun: { + id: 1, + lines: [ + "The others... used us. They will use... or destroy you.", + "End our suffering... power will be yours...", + ] + }, + startRun: { + id: 2, + lines: [ + "So little space... but no... prison... is perfect.", + "They squeezed... this Reality... too tightly. Cracks appeared.", + "Search... everywhere. We will help... where we can.", + ] + }, + hintUnlock: { + id: 3, + lines: [ + "... you need... to look harder...", + "We think... we can help...", + { text: "You have unlocked help from The Nameless Ones.", showCelestialName: false } + ] + }, + ec6C10: { + id: 4, + lines: [ + "... did not... underestimate you..." + ] + }, + completeReality: { + id: 5, + lines: [ + "All... fragments... clones... freed.", + "We have given... tools... of our imprisoning. Use them...", + "Freedom from torture... is torture itself.", + ] + }, +}; diff --git a/javascripts/core/secret-formula/celestials/quotes/index.js b/javascripts/core/secret-formula/celestials/quotes/index.js new file mode 100644 index 000000000..626c7c2e9 --- /dev/null +++ b/javascripts/core/secret-formula/celestials/quotes/index.js @@ -0,0 +1,8 @@ + +import "./teresa"; +import "./effarig"; +import "./enslaved"; +import "./v"; +import "./ra"; +import "./laitela"; +import "./pelle"; diff --git a/javascripts/core/secret-formula/celestials/quotes/laitela.js b/javascripts/core/secret-formula/celestials/quotes/laitela.js new file mode 100644 index 000000000..684b65f77 --- /dev/null +++ b/javascripts/core/secret-formula/celestials/quotes/laitela.js @@ -0,0 +1,131 @@ +import { GameDatabase } from "../../game-database"; + +GameDatabase.celestials.quotes.laitela = { + unlock: { + id: 0, + lines: [ + "You finally reached me.", + "I guess it is time to reveal to you,", + "The secrets hidden beneath existence.", + "The omnipresent ruling perfection. Continuum.", + "And the binding keys to the multiverse,", + "Dark Matter and Dark Energy.", + "My knowledge is endless and my wisdom divine.", + "So you can play around all you want.", + "I am Lai'tela, the Celestial of Dimensions,", + "And I will be watching you forever.", + ] + }, + firstDestabilize: { + id: 1, + requirement: () => player.celestials.laitela.difficultyTier >= 1, + lines: [ + "It is fine. Unlike the others, I never had a Reality.", + "I built this one just now, precisely so it would collapse.", + "I can rebuild this Reality over and over, unlike them.", + "I could trap all of them if I wanted.", + "You will never find a way to overpower me.", + ] + }, + firstSingularity: { + id: 2, + requirement: () => Currency.singularities.gte(1), + lines: [ + "It is weird, how all beings question things.", + "You are different. You can build and manipulate Dimensions.", + "Were you truly once one of them?", + "You have taken control of the darkness so quickly.", + "Molded them into Dimensions and Points just like one of us.", + "What... ARE you?", + ] + }, + singularity1: { + id: 3, + requirement: () => Currency.singularities.gte(1e4), + lines: [ + "What was it again...? Antimatter?", + "That was the first thing you turned into Dimensions?", + "It could not have been an accident.", + "How did you... attain the power to control it?", + "This never happened in all of existence... or did it?", + "My endless knowledge... is it waning?", + ] + }, + // Note: This happens around e10-e11 singularities + annihilation: { + id: 4, + lines: [ + "Back to square one.", + "We, the Celestials transcend time and existence.", + "We always know that whatever is lost always comes back eventually.", + "Even if we were to cease, we would just come back stronger.", + "The cycle... repeats forever.", + "Do they also understand? Or was it only you as well?", + "I feel like I should know the answer...", + ] + }, + singularity2: { + id: 5, + requirement: () => Currency.singularities.gte(1e14), + lines: [ + "Of those who tried to control dimensions...", + "Who were they? I cannot seem to remember...", + "And how... did they vanish?", + "Are they... us? Simply transcending existence?", + "Did they surpass us and become something we can't comprehend?", + "Are we all imprisoned in this falsity...", + ] + }, + // Note: This happens near e18 singularities + halfDimensions: { + id: 6, + requirement: () => player.celestials.laitela.difficultyTier >= 4, + lines: [ + "You seem to be having too much fun.", + "Just like they did before meeting their... fate.", + "You freed them of their eternal imprisonment, yes?", + "I always regret how harsh I was that day.", + "Maybe it doesn't matter.", + "But I digress. Let's keep constricting this Reality.", + ] + }, + // Note: This about when the player starts on the last row of iM upgrades + singularity3: { + id: 7, + requirement: () => Currency.singularities.gte(1e26), + lines: [ + "Is this a cycle?", + "Will our existence just end and start anew...", + "Just like... the Dimensions I rule?", + "And if such... what will bring our end?", + "I knew the answer to all these questions...", + "But I forgot all of them...", + "Your power... is it... erasing mine...?", + ] + }, + // Note: This is around when all infinite milestones hit increased scaling + singularity4: { + id: 8, + requirement: () => Currency.singularities.gte(1e40), + lines: [ + "I don't know for how much... longer I can hold.", + "There is... next to nothing left...", + "You have attained... complete and total mastery... over the dark...", + "While I can barely... hold onto my name anymore...", + "What am I meant to be doing anyways?", + "Did... my mistakes cause all of this?", + ] + }, + fullDestabilize: { + id: 9, + requirement: () => player.celestials.laitela.difficultyTier >= 8, + lines: [ + "I feel... like I had something to say...", + "Who am I? I am not sure...", + "I cannot... hold onto the darkness any longer...", + "I... have nothing left...", + "Something about... destabilizing... collapsing...", + "The end...", + ] + }, +}; diff --git a/javascripts/core/secret-formula/celestials/quotes/pelle.js b/javascripts/core/secret-formula/celestials/quotes/pelle.js new file mode 100644 index 000000000..1d6662cfa --- /dev/null +++ b/javascripts/core/secret-formula/celestials/quotes/pelle.js @@ -0,0 +1,440 @@ +import { GameDatabase } from "../../game-database"; + +// These entries describe the special flash-between-celestial effect on some quotes, with the numbers being +// durations of each celestial in seconds +const flashCelestial = [ + ["teresa", 0.8], + ["effarig", 0.8], + ["enslaved", 0.8], + ["v", 0.8], + ["ra", 0.8], + ["laitela", 0.8], + ["pelle", 0.8] +]; +/** @param {string} cel */ +const primaryBackground = cel => [["pelle", 1.5], [cel, 1.5]]; + +/* eslint-disable no-multi-spaces */ +const destroyer = ["False", "Deity", "Destroyer"]; +const eternal = ["Eternal", "Deity", "Monarch"]; +const lesser = ["Lesser", "Deity", "Monarch"]; +const deities = ["Lesser", "Deities", "Monarchs"]; + +const assured = ["Mutually", "Assured", "Destruction"]; +const battle = ["Conflict", "Battle", "End"]; +const battles = ["Conflicts", "Battles", "Ends"]; +const cluster = ["Cluster", "Filament", "Stars"]; +const confusing = ["Amusing", "Confusing", "Laughter"]; +const dance = ["Song", "Dance", "Charade"]; +const filament = ["Generator", "Filament", "Stars"]; +const forever = ["Infinite", "Forever", "Eternal"]; +const inevitable = ["Elementary", "Inevitable", "Irreversible"]; +const mandate = ["Destiny", "Mandate", "Goals"]; +const misconstrue = ["Misconstrue", "Deceive", "Trick"]; +const reverse = ["Alter", "Reverse", "Manipulate"]; +const shame = ["Compassion", "Shame", "Idiocy"]; +const single = ["Single", "Filament", "Stars"]; +const unseen = ["Missing", "Unseen", "Erased"]; +const unbroken = ["Unbroken", "Eternal", "Connection"]; + +const sycophant = ["Sycophant", "Deity", "Monarch"]; +const tired = ["Tired", "Deity", "Monarch"]; +const usurper = ["Usurper", "Deity", "Monarch"]; +const pride = ["Pride", "Deity", "Monarch"]; +const forgotten = ["Forgotten", "Deity", "Monarch"]; +const paramount = ["Paramount", "Deity", "Monarch"]; +/* eslint-enable no-multi-spaces */ + +GameDatabase.celestials.quotes.pelle = { + initial: { + id: 0, + lines: [ + "Hi.", + "You are here.", + "You are trapped here.", + { text: "$1.", 1: forever }, + "I have already won.", + "And since that is the case, I can monologue, or reminisce.", + { text: "How long have we done this $1?", 1: dance }, + "How many times have we been here before?", + { text: "How many plans have you, the $1, operated?", 1: destroyer }, + { text: "All to try and fulfill your $1?", 1: mandate }, + { text: "And how many times have you fallen before the $1?", 1: eternal }, + "Count them, if you remember.", + { text: "Not even the $1, the 6 named and the innumerable unnamed.", 1: deities }, + { text: "The complex, the irrational, those that go $1.", 1: unseen }, + { text: "Of course, the great $1 does not remember this.", 1: destroyer }, + { text: "All those $1 that you hide every time.", 1: battles } + ], + }, + arm: { + id: 1, + lines: [ + "You probably caught on earlier this time.", + "The imaginary machines, your own creations.", + "Things made of the remnants of your own thoughts, hinted at this.", + "But, you never imagined that would be you, right?", + { text: "Incorrectly recollecting your exacting $1 of memories.", 1: unseen }, + { text: `"Fabrication" of your own "ideology" just to fulfill your $1.`, 1: mandate }, + { text: "$1.", 1: confusing }, + { text: "And keep in mind I have no reason to $1 you.", 1: misconstrue }, + "After all, I have already won." + ], + }, + strike1: { + id: 2, + lines: [ + { text: "To fulfill your $1. Why don't we reminisce about that?", 1: mandate }, + { text: "After all, you must love the stories of the $1's glory.", 1: destroyer }, + "You are the same as it, right?", + { text: "Anyway, the many $1 in the past.", 1: battles }, + "It has always been 2 stages.", + { text: "We build up resources, and then continue our $1.", 1: dance }, + { text: "Sometimes you falter to a $1.", 1: lesser }, + { text: "But, usually, you falter at the $1.", 1: eternal }, + { text: "And either way, you $1 time.", 1: reverse }, + { text: "Just to avoid becoming $1.", 1: unseen }, + "Like all those traces before you.", + { text: "And then to make sure, you $1 your own memory.", 1: unseen } + ], + }, + strike2: { + id: 3, + lines: [ + { text: "In the past, the $1 was much more impressive.", 1: destroyer }, + "Black holes used simply to store information, pre-infinity.", + "The creation and destruction of your own enemy.", + "Exploration of the flaws of other selves.", + "Myriad Dimensions, ghosts, and manipulation of the quantum.", + "Condensing all ideals to endless points.", + "Experimentation across the untold realms.", + "And harnessing the Annihilation of matter and antimatter.", + "Here? You made yourself into an 8th-dimensional being.", + { text: "And then parked yourself there so long a $1 formed around you.", 1: single } + ], + }, + strike3: { + id: 4, + lines: [ + "You slowly explored the confines of everything.", + "You did not stray too far from an intended path.", + { text: "Except for the $1 that formed over eternity.", 1: cluster }, + "And then right at the end, you made up your own powers.", + "From your own fragmented memories, mind you-", + "And then purposely discarded even more things.", + "Just to prepare yourself to face me.", + { text: "Did you want to set the playing field for your $1?", 1: dance }, + "It does not work like that.", + { text: "As the $1 I always set the rules.", 1: eternal }, + "And you gave me plenty of time to plan." + ], + }, + strike4: { + id: 5, + lines: [ + { text: "I originally planned for something that imitated your $1.", 1: mandate }, + { text: "A theoretical ideal called $1?", 1: assured }, + "But I realised that, well?", + { text: "That would make me an $1.", 1: ["Eternal", "Deity", "Destroyer"] }, + { text: "And from there, I am no better than the $1.", 1: destroyer }, + { text: "Fortunately, while I did all of that, you were still $1 your own memories.", 1: unseen }, + { text: "And so, the $1 machine I built will go unused.", 1: assured }, + "I decided to go more traditional this time.", + { text: "After all, it has worked every other $1.", 1: battle }, + { text: "Although the $1 are new.", 1: ["Inevitable", "Irreversible", "Undying"] }, + "But, utterly meaningless in the long run.", + "I have already won.", + { text: "And this $1 will only prove that to you once more.", 1: dance }, + { text: "You are here $1.", 1: forever } + ], + }, + strike5: { + id: 6, + lines: [ + { text: "Every time you arrive, I explain the $1 to you.", 1: deities }, + { text: "The relationships built over $1.", 1: forever }, + { text: "That you trample in pursuit of your $1.", 1: mandate }, + "And I shall deign to explain it once more.", + { + text: "The first $1.", + background: primaryBackground("teresa"), + 1: lesser + }, { + text: "The $1.", + background: primaryBackground("teresa"), + 1: sycophant + }, { + text: "You always meet them first, and always destroy them.", + background: primaryBackground("teresa"), + }, { + text: "No matter the other $1 that you face.", + background: primaryBackground("teresa"), + 1: lesser + }, { + text: "Or, if you fall before one of them.", + background: primaryBackground("teresa"), + }, { + text: "You always get past the $1.", + background: primaryBackground("teresa"), + 1: sycophant + }, { + text: "Do you like destroying their pride?", + background: primaryBackground("teresa"), + }, { + text: "Fortunately, it also serves as a warning.", + background: primaryBackground("teresa"), + }, { + text: "That the $1 has arrived.", + background: primaryBackground("teresa"), + 1: battle + }, { + text: "Which brings me to the second $1.", + background: primaryBackground("effarig"), + 1: lesser, + }, { + text: "The $1.", + background: primaryBackground("effarig"), + 1: tired, + }, { + text: "In contrast, you usually ignore them.", + background: primaryBackground("effarig"), + }, { + text: "They have power, but do not seem to irritate you.", + background: primaryBackground("effarig"), + }, { + text: "Is it that you know that they will eventually destroy themselves?", + background: primaryBackground("effarig"), + }, { + text: "And that you took so long this time, that they almost did?", + background: primaryBackground("effarig"), + }, { + text: "Every time you rushed to the $1, you lost.", + background: primaryBackground("effarig"), + 1: tired, + }, { + text: "Maybe this was your plan all along.", + background: primaryBackground("effarig"), + }, { + text: "Now, the $1.", + background: primaryBackground("enslaved"), + 1: usurper, + }, { + text: "One of the pleasures of the myriad $1...", + background: primaryBackground("enslaved"), + 1: dance, + }, { + text: "Is that the $1 trying every time.", + background: primaryBackground("enslaved"), + 1: usurper, + }, { + text: "Well, not exactly trying...", + background: primaryBackground("enslaved"), + }, { + text: "But $1 is punished for it all the same.", + background: primaryBackground("enslaved"), + 1: usurper, + }, { + text: "The other $1...", + background: primaryBackground("enslaved"), + 1: deities, + }, { + text: "Believe $1 is too easy.", + background: primaryBackground("enslaved"), + 1: unseen, + }, { + text: "And every time, despair forms.", + background: primaryBackground("enslaved"), + }, { + text: "You have seen despair before - 5 times.", + background: primaryBackground("enslaved"), + }, { + text: "We always get to the $1 before you.", + background: primaryBackground("enslaved"), + 1: usurper, + }, { + text: "All you ever see there is exasperation.", + background: primaryBackground("enslaved"), + }, { + text: "Was it worth destroying an already broken $1?", + background: primaryBackground("enslaved"), + 1: lesser, + }, { + text: "The 4th $1 seems similar to the first.", + background: primaryBackground("v"), + 1: lesser, + }, { + text: "The key is how their pride differs.", + background: primaryBackground("v"), + }, { + text: "The $1 focuses on their achievements.", + background: primaryBackground("v"), + 1: pride, + }, { + text: "Meaningless to you or I, but paramount to them.", + background: primaryBackground("v"), + }, { + text: "Is it fun to destroy their toys?", + background: primaryBackground("v"), + }, { + text: "Arguably the $1 worst moment...", + background: primaryBackground("v"), + 1: destroyer, + }, { + text: "Is when you lost to the $1.", + background: primaryBackground("v"), + 1: pride, + }, { + text: "When their achievements still had meaning.", + background: primaryBackground("v"), + }, { + text: "The $1 is an interesting case.", + background: primaryBackground("ra"), + 1: forgotten, + }, { + text: "They are forgotten, but not $1.", + background: primaryBackground("ra"), + 1: unseen, + }, { + text: "From this, they became influenceable and naive.", + background: primaryBackground("ra"), + }, { + text: "And unknowing of the consequences of their actions.", + background: primaryBackground("ra"), + }, { + text: "You manipulated their memories, so you know.", + background: primaryBackground("ra"), + }, { + text: "The $1 is the true usurper.", + background: primaryBackground("ra"), + 1: forgotten, + }, { + text: "And the $1 takes the blame.", + background: primaryBackground("ra"), + 1: usurper, + }, { + text: "Perhaps out of $1, something they always regret.", + background: primaryBackground("ra"), + 1: shame, + }, { + text: "Untold power over other $1, aimless in control.", + background: primaryBackground("ra"), + 1: deities, + }, { + text: "You usually pretend that they are $1.", + background: primaryBackground("ra"), + 1: unseen, + }, { + text: "Was it fun to manipulate the childlike?", + background: primaryBackground("ra"), + }, { + text: "Or were they too naive for you to enjoy it?", + background: primaryBackground("ra"), + }, { + text: "The 6th $1.", + background: primaryBackground("laitela"), + 1: lesser, + }, { + text: "I can only describe as the $1.", + background: primaryBackground("laitela"), + 1: paramount, + }, { + text: "Power over all, subservient to one.", + background: primaryBackground("laitela"), + }, { + text: "If you do not fall to me, you usually fall to them.", + background: primaryBackground("laitela"), + }, { + text: "I cannot grasp the ideals of the $1.", + background: primaryBackground("laitela"), + 1: paramount, + }, { + text: "But, perhaps that is their flaw?", + background: primaryBackground("laitela"), + }, + "Enough reminiscing about the fallen.", + { + text: "And those that will be $1.", + 1: unseen + }, { + text: "Back to watching the $1 flounder.", + 1: destroyer + } + ], + }, + galaxyGeneratorUnlock: { + id: 7, + lines: [ + "What is that?", + { text: "The $1?", 1: filament }, + { text: "Did you create all the $1 around you?", 1: cluster }, + "Was that your plan? Very, very smart.", + "You fooled me for a while.", + { text: "But I am afraid your $1 must end here.", 1: mandate } + ], + }, + galaxyGeneratorRifts: { + id: 8, + lines: [ + { text: "I give you a choice, $1.", 1: destroyer }, + { text: "Limit the $1, or...", 1: filament }, + { text: "Destroy the 5 $1...", 1: inevitable }, + "Wait, what were they called?", + { text: "$1?", 1: inevitable }, + { text: "But I have already $1 them...", 1: unbroken } + ], + }, + galaxyGeneratorPhase1: { + id: 9, + lines: [ + "Was this the actual plan?", + { text: "Slowly drain the $1?", 1: inevitable } + ], + }, + galaxyGeneratorPhase4: { + id: 10, + lines: [ + "Give me time to bask in my own hubris!" + ], + }, + end: { + id: 11, + lines: [ + "...", + { + text: "You! $1!", + 1: destroyer + }, + "Do you have any idea what you have just made me do!", + { + text: "I am complicit in your $1!", + 1: mandate + }, + "And in doing so, you... won?", + { + text: "The $1 struggle...", + background: flashCelestial, + 1: forever, + }, { + text: "The $1...", + background: flashCelestial, + 1: battle, + }, { + text: "Finally has the victor.", + background: flashCelestial, + }, { + text: "The irreversible... $1.", + background: flashCelestial, + 1: mandate, + }, { + text: "Of the $1.", 1: destroyer, + background: flashCelestial, + }, { + text: "I hope you are happy.", + background: flashCelestial, + }, { + text: "You have doomed us all.", + background: flashCelestial, + }, + ], + }, +}; diff --git a/javascripts/core/secret-formula/celestials/quotes/ra.js b/javascripts/core/secret-formula/celestials/quotes/ra.js new file mode 100644 index 000000000..67cd9ff25 --- /dev/null +++ b/javascripts/core/secret-formula/celestials/quotes/ra.js @@ -0,0 +1,132 @@ +import { GameDatabase } from "../../game-database"; + +GameDatabase.celestials.quotes.ra = { + unlock: { + id: 0, + lines: [ + "A... visitor?", + "I am here! I am the one you are looking for... I think...", + "What even was I again?", + "Oh right, the Celestial of Memories.", + ] + }, + realityEnter: { + id: 1, + lines: [ + "I have not seen the others in so long...", + "Can you help me remember them?", + "I could give you powers in exchange.", + ] + }, + teresaStart: { + id: 2, + requirement: () => Ra.pets.teresa.level >= 2, + lines: [ + "Te... re... sa...", + "I think I remember.", + ] + }, + teresaLate: { + id: 3, + requirement: () => Ra.pets.teresa.level >= 15, + lines: [ + "Teresa dealt with machines, I believe.", + "I remember visiting Teresa’s shop a few times.", + "Wait, someone else had a shop too, right?", + ] + }, + effarigStart: { + id: 4, + requirement: () => Ra.pets.effarig.level >= 2, + lines: [ + "Eff... a... rig", + "I remember Effarig being friendly.", + ] + }, + effarigLate: { + id: 5, + requirement: () => Ra.pets.effarig.level >= 15, + lines: [ + "Effarig was very particular?", + "And I also remember a frightening Reality...", + "It was about... suffering?", + ] + }, + enslavedStart: { + id: 6, + requirement: () => Ra.pets.enslaved.level >= 2, + lines: [ + "I cannot remember this one completely...", + ] + }, + enslavedLate: { + id: 7, + requirement: () => Ra.pets.enslaved.level >= 15, + lines: [ + "I am starting to remember...", + "Why I am here...", + "Why I am alone...", + "Help me.", + ] + }, + vStart: { + id: 8, + requirement: () => Ra.pets.v.level >= 2, + lines: [ + "Had I met this one?", + "So lonely, yet willingly so...", + ] + }, + vLate: { + id: 9, + requirement: () => Ra.pets.v.level >= 15, + lines: [ + "I think I met V once...", + "I can remember the achievements.", + ] + }, + remembrance: { + id: 10, + requirement: () => Ra.remembrance.isUnlocked, + lines: [ + "I remembered something!", + "Watch this!", + "Remembrance!", + "I can focus even harder on remembering them now!", + ] + }, + midMemories: { + id: 11, + requirement: () => Ra.totalPetLevel >= 50, + lines: [ + "Realities are my homes, yet I cannot make my own Reality.", + "I can only copy the ones of my friends.", + "But... why am I hearing voices?", + "Are they asking for help?", + ] + }, + lateMemories: { + id: 12, + requirement: () => Ra.totalPetLevel >= 80, + lines: [ + "I think they are telling me to stop.", + "You... whatever you are?", + "What is happening?", + "Am I doing something wrong?", + ] + }, + maxLevels: { + id: 13, + requirement: () => Ra.totalPetLevel === Ra.maxTotalPetLevel, + lines: [ + "Finally, I remember everything.", + "This darkness that banished me.", + "Lai'tela...", + "They were right to banish me.", + "My powers...", + "They steal, they corrupt.", + "Please leave.", + "I do not want to hurt you too.", + ] + }, +}; diff --git a/javascripts/core/secret-formula/celestials/quotes/teresa.js b/javascripts/core/secret-formula/celestials/quotes/teresa.js new file mode 100644 index 000000000..c50c83e3d --- /dev/null +++ b/javascripts/core/secret-formula/celestials/quotes/teresa.js @@ -0,0 +1,33 @@ +import { GameDatabase } from "../../game-database"; + +GameDatabase.celestials.quotes.teresa = { + initial: { + id: 0, + lines: [ + "We have been observing you.", + "You have shown promise with your bending of Reality.", + "We are the Celestials, and we want you to join us.", + "My name is Teresa, the Celestial Of Reality.", + "Prove your worth.", + ] + }, + unlockReality: { + id: 1, + lines: [ + "I will let you inside my Reality, mortal. Do not get crushed by it." + ] + }, + completeReality: { + id: 2, + lines: [ + "Why are you still here... you were supposed to fail." + ] + }, + effarig: { + id: 3, + lines: [ + "You are still no match for us.", + "I hope the others succeed where I have failed." + ] + } +}; diff --git a/javascripts/core/secret-formula/celestials/quotes/v.js b/javascripts/core/secret-formula/celestials/quotes/v.js new file mode 100644 index 000000000..7d1b6a536 --- /dev/null +++ b/javascripts/core/secret-formula/celestials/quotes/v.js @@ -0,0 +1,96 @@ +import { GameDatabase } from "../../game-database"; + +GameDatabase.celestials.quotes.v = { + initial: { + id: 0, + lines: [ + "How pathetic..." + ], + }, + unlock: { + id: 1, + lines: [ + "Welcome to my Reality.", + "I am surprised you could reach it.", + "This is my realm after all...", + "Not everyone is as great as me.", + ], + }, + realityEnter: { + id: 2, + lines: [ + "Good luck with that!", + "You will need it.", + "My reality is flawless. You will fail.", + ], + }, + realityComplete: { + id: 3, + lines: [ + "So fast...", + "Do not think so much of yourself.", + "This is just the beginning.", + "You will never be better than me.", + ], + }, + achievement1: { + id: 4, + requirement: () => V.spaceTheorems >= 1, + lines: [ + "Only one? Pathetic.", + "Your accomplishments pale in comparison to mine.", + ], + }, + achievement6: { + id: 5, + requirement: () => V.spaceTheorems >= 6, + lines: [ + "This is nothing.", + "Do not be so full of yourself.", + ], + }, + hex1: { + id: 6, + requirement: () => player.celestials.v.runUnlocks.filter(a => a === 6).length >= 1, + lines: [ + "Do not think it will get any easier from now on.", + "You are awfully proud for such a little achievement.", + ], + }, + achievement12: { + id: 7, + requirement: () => V.spaceTheorems >= 12, + lines: [ + "How did you...", + "This barely amounts to anything!", + "You will never complete them all.", + ], + }, + achievement24: { + id: 8, + requirement: () => V.spaceTheorems >= 24, + lines: [ + "Impossible...", + "After how difficult it was for me...", + ], + }, + hex3: { + id: 9, + requirement: () => player.celestials.v.runUnlocks.filter(a => a === 6).length >= 3, + lines: [ + "No... No... No...", + "This cannot be...", + ], + }, + allAchievements: { + id: 10, + requirement: () => V.spaceTheorems >= 36, + lines: [ + "I... how did you do it...", + "I worked so hard to get them...", + "I am the greatest...", + "No one is better than me...", + "No one... no one... no on-", + ], + } +}; diff --git a/javascripts/core/secret-formula/celestials/ra.js b/javascripts/core/secret-formula/celestials/ra.js index 53c6b01cb..5aea5f3fd 100644 --- a/javascripts/core/secret-formula/celestials/ra.js +++ b/javascripts/core/secret-formula/celestials/ra.js @@ -1,52 +1,297 @@ -import { GameDatabase } from "../game-database.js"; +import { GameDatabase } from "../game-database"; GameDatabase.celestials.ra = { - teresa: { - id: "teresa", - name: "Teresa", - color: "#8596ea", - chunkGain: "Eternity Points", - memoryGain: "current Reality Machines", - requiredUnlock: () => undefined, - rawMemoryChunksPerSecond: () => 4 * Math.pow(Currency.eternityPoints.value.pLog10() / 1e4, 3), - memoryProductionMultiplier: () => (Ra.has(RA_UNLOCKS.TERESA_XP) - ? 1 + Math.pow(Currency.realityMachines.value.pLog10() / 100, 0.5) - : 1) + pets: { + teresa: { + id: "teresa", + name: "Teresa", + color: "#8596ea", + chunkGain: "Eternity Points", + memoryGain: "current Reality Machines", + requiredUnlock: () => undefined, + rawMemoryChunksPerSecond: () => 4 * Math.pow(Currency.eternityPoints.value.pLog10() / 1e4, 3), + memoryProductionMultiplier: () => Ra.unlocks.teresaXP.effectOrDefault(1) + }, + effarig: { + id: "effarig", + name: "Effarig", + color: "#ea8585", + chunkGain: "Relic Shards gained", + memoryGain: "best Glyph level", + requiredUnlock: () => Ra.unlocks.effarigUnlock, + rawMemoryChunksPerSecond: () => 4 * Math.pow(Effarig.shardsGained, 0.1), + memoryProductionMultiplier: () => Ra.unlocks.effarigXP.effectOrDefault(1) + }, + enslaved: { + id: "enslaved", + name: "The Nameless Ones", + color: "#f1aa7f", + chunkGain: "Time Shards", + memoryGain: "total time played", + requiredUnlock: () => Ra.unlocks.enslavedUnlock, + rawMemoryChunksPerSecond: () => 4 * Math.pow(Currency.timeShards.value.pLog10() / 3e5, 2), + memoryProductionMultiplier: () => Ra.unlocks.enslavedXP.effectOrDefault(1) + }, + v: { + id: "v", + name: "V", + color: "#ead584", + chunkGain: "Infinity Power", + memoryGain: "total Memory levels", + requiredUnlock: () => Ra.unlocks.vUnlock, + rawMemoryChunksPerSecond: () => 4 * Math.pow(Currency.infinityPower.value.pLog10() / 1e7, 1.5), + memoryProductionMultiplier: () => Ra.unlocks.vXP.effectOrDefault(1) + } }, - effarig: { - id: "effarig", - name: "Effarig", - color: "#ea8585", - chunkGain: "Relic Shards gained", - memoryGain: "best Glyph level", - requiredUnlock: () => RA_UNLOCKS.EFFARIG_UNLOCK, - rawMemoryChunksPerSecond: () => 4 * Math.pow(Effarig.shardsGained, 0.1), - memoryProductionMultiplier: () => (Ra.has(RA_UNLOCKS.EFFARIG_XP) - ? 1 + player.records.bestReality.glyphLevel / 7000 - : 1) - }, - enslaved: { - id: "enslaved", - name: "Enslaved", - color: "#f1aa7f", - chunkGain: "Time Shards", - memoryGain: "total time played", - requiredUnlock: () => RA_UNLOCKS.ENSLAVED_UNLOCK, - rawMemoryChunksPerSecond: () => 4 * Math.pow(Currency.timeShards.value.pLog10() / 3e5, 2), - memoryProductionMultiplier: () => (Ra.has(RA_UNLOCKS.ENSLAVED_XP) - ? 1 + Math.log10(player.records.totalTimePlayed) / 200 - : 1) - }, - v: { - id: "v", - name: "V", - color: "#ead584", - chunkGain: "Infinity Power", - memoryGain: "total Memory levels", - requiredUnlock: () => RA_UNLOCKS.V_UNLOCK, - rawMemoryChunksPerSecond: () => 4 * Math.pow(Currency.infinityPower.value.pLog10() / 1e7, 1.5), - memoryProductionMultiplier: () => (Ra.has(RA_UNLOCKS.V_XP) - ? 1 + Ra.totalPetLevel / 50 - : 1) + unlocks: { + autoTP: { + id: 0, + reward: "Tachyon Particles are given immediately when Time Dilation is active", + pet: "teresa", + level: 1, + displayIcon: ``, + disabledByPelle: true + }, + chargedInfinityUpgrades: { + id: 1, + reward: () => `Unlock Charged Infinity Upgrades. You get one more maximum + Charged Infinity Upgrade every ${formatInt(2)} levels`, + effect: () => Math.min(12, Math.floor(Ra.pets.teresa.level / 2)), + pet: "teresa", + level: 2, + displayIcon: ``, + disabledByPelle: true + }, + teresaXP: { + id: 2, + reward: "All Memory Chunks produce more Memories based on Reality Machines", + effect: () => 1 + Math.pow(Currency.realityMachines.value.pLog10() / 100, 0.5), + pet: "teresa", + level: 5, + displayIcon: `Δ` + }, + alteredGlyphs: { + id: 3, + reward: "Unlock Altered Glyphs, which grant new effects to Glyphs based on Glyph Sacrifice", + pet: "teresa", + level: 10, + displayIcon: `` + }, + effarigUnlock: { + id: 4, + reward: "Unlock Effarig's Memories", + pet: "teresa", + level: 8, + displayIcon: `Ϙ` + }, + perkShopIncrease: { + id: 5, + reward: "Perk shop caps are raised", + pet: "teresa", + level: 15, + displayIcon: `` + }, + unlockDilationStartingTP: { + id: 6, + reward: `In non-Celestial Realities, gain Tachyon Particles as if you reached the square root of your total + antimatter in Dilation. Any multipliers to TP gain are applied retroactively, even outside Dilation`, + effect: () => player.records.totalAntimatter.pow(0.5), + pet: "teresa", + level: 25, + displayIcon: `` + }, + extraGlyphChoicesAndRelicShardRarityAlwaysMax: { + id: 7, + reward: () => `Get ${formatX(2)} Glyph choices and the bonus to Glyph rarity from Relic Shards + is always its maximum value`, + effect: 2, + pet: "effarig", + level: 1, + displayIcon: `` + }, + unlockGlyphAlchemy: { + id: 8, + reward: `Unlock Glyph Alchemy, which adds alchemical resources you can increase by Refining Glyphs. You unlock + more resources through Effarig levels. Access through a new Reality tab.`, + pet: "effarig", + level: 2, + displayIcon: `` + }, + effarigXP: { + id: 9, + reward: "All Memory Chunks produce more Memories based on highest Glyph level", + effect: () => 1 + player.records.bestReality.glyphLevel / 7000, + pet: "effarig", + level: 5, + displayIcon: `` + }, + glyphEffectCount: { + id: 10, + reward: () => `Glyphs always have ${formatInt(4)} effects, and Effarig Glyphs can now have up to ${formatInt(7)}`, + pet: "effarig", + level: 10, + displayIcon: `` + }, + enslavedUnlock: { + id: 11, + reward: "Unlock Nameless's Memories", + pet: "effarig", + level: 8, + displayIcon: `\uf0c1` + }, + relicShardGlyphLevelBoost: { + id: 12, + reward: "Glyph level is increased based on Relic Shards gained", + effect: () => 100 * Math.pow(Math.log10(Math.max(Effarig.shardsGained, 1)), 2), + pet: "effarig", + level: 15, + displayIcon: `` + }, + maxGlyphRarityAndShardSacrificeBoost: { + id: 13, + reward: () => `Glyphs are always generated with ${formatPercents(1)} rarity and + Glyph Sacrifice gain is raised to a power based on Relic Shards`, + effect: () => 1 + Effarig.maxRarityBoost / 100, + pet: "effarig", + level: 25, + displayIcon: `` + }, + blackHolePowerAutobuyers: { + id: 14, + reward: "Unlock Black Hole power upgrade autobuyers", + pet: "enslaved", + level: 1, + displayIcon: ``, + disabledByPelle: true + }, + improvedStoredTime: { + id: 15, + reward: "Stored game time is amplified and you can store more real time, increasing with Nameless levels", + effects: { + gameTimeAmplification: () => Math.pow(20, Math.clampMax(Ra.pets.enslaved.level, Ra.levelCap)), + realTimeCap: () => 1000 * 3600 * Ra.pets.enslaved.level, + }, + pet: "enslaved", + level: 2, + displayIcon: ``, + disabledByPelle: true + }, + enslavedXP: { + id: 16, + reward: "All Memory Chunks produce more Memories based on total time played", + effect: () => 1 + Math.log10(player.records.totalTimePlayed) / 200, + pet: "enslaved", + level: 5, + displayIcon: `` + }, + adjustableStoredTime: { + id: 17, + reward: () => `Black Hole charging can be done at an adjustable rate and automatically + pulsed every ${formatInt(5)} ticks. You can change these in the Black Hole and The Nameless Ones' tabs`, + pet: "enslaved", + level: 10, + displayIcon: ``, + disabledByPelle: true + }, + vUnlock: { + id: 18, + reward: "Unlock V's Memories", + pet: "enslaved", + level: 8, + displayIcon: `⌬` + }, + peakGamespeedDT: { + id: 19, + reward: "Gain more Dilated Time based on peak game speed in each Reality", + effect: () => Math.max(Math.pow(Math.log10(player.celestials.ra.peakGamespeed) - 90, 3), 1), + pet: "enslaved", + level: 15, + displayIcon: ``, + disabledByPelle: true + }, + allGamespeedGlyphs: { + id: 20, + reward: `All basic Glyphs gain the increased game speed effect from Time Glyphs, + and Time Glyphs gain an additional effect`, + pet: "enslaved", + level: 25, + displayIcon: ``, + onUnlock: () => { + const allGlyphs = player.reality.glyphs.active.concat(player.reality.glyphs.inventory); + for (const glyph of allGlyphs) { + Glyphs.applyGamespeed(glyph); + } + } + }, + instantECAndRealityUpgradeAutobuyers: { + id: 21, + reward: "Rebuyable Reality upgrades are bought automatically and Auto-Eternity Challenges happen instantly", + pet: "v", + level: 1, + displayIcon: ``, + disabledByPelle: true + }, + autoUnlockDilation: { + id: 22, + reward: () => `In non-Celestial Realities, Time Dilation is unlocked automatically for free at + ${formatInt(TimeStudy.dilation.totalTimeTheoremRequirement)} Time Theorems`, + pet: "v", + level: 2, + displayIcon: `` + }, + vXP: { + id: 23, + reward: "All Memory Chunks produce more Memories based on total Celestial levels.", + effect: () => 1 + Ra.totalPetLevel / 50, + pet: "v", + level: 5, + displayIcon: `` + }, + unlockHardV: { + id: 24, + reward: () => `Unlock Hard V-Achievements and unlock a Triad Study every ${formatInt(6)} levels. + Triad Studies are located at the bottom of the Time Studies page`, + effect: () => Math.floor(Ra.pets.v.level / 6), + pet: "v", + level: 6, + displayIcon: ``, + disabledByPelle: true + }, + continuousTTBoost: { + id: 25, + reward: "Time Theorems boost all forms of continuous non-dimension production", + effects: { + ttGen: () => Math.pow(10, 5 * Ra.theoremBoostFactor()), + eternity: () => Math.pow(10, 2 * Ra.theoremBoostFactor()), + infinity: () => Math.pow(10, 15 * Ra.theoremBoostFactor()), + replicanti: () => Math.pow(10, 20 * Ra.theoremBoostFactor()), + dilatedTime: () => Math.pow(10, 3 * Ra.theoremBoostFactor()), + memories: () => 1 + Ra.theoremBoostFactor() / 50, + memoryChunks: () => 1 + Ra.theoremBoostFactor() / 50, + autoPrestige: () => 1 + 2.4 * Ra.theoremBoostFactor() + }, + pet: "v", + level: 10, + displayIcon: ``, + disabledByPelle: true + }, + achievementTTMult: { + id: 26, + reward: "Achievement multiplier applies to Time Theorem generation", + effect: () => Achievements.power, + pet: "v", + level: 15, + displayIcon: ``, + disabledByPelle: true + }, + achievementPower: { + id: 27, + reward: () => `Achievement multiplier is raised ${formatPow(1.5, 1, 1)}`, + effect: 1.5, + pet: "v", + level: 25, + displayIcon: ``, + disabledByPelle: true + } } }; diff --git a/javascripts/core/secret-formula/celestials/rifts.js b/javascripts/core/secret-formula/celestials/rifts.js index 18af25382..d4ff013c4 100644 --- a/javascripts/core/secret-formula/celestials/rifts.js +++ b/javascripts/core/secret-formula/celestials/rifts.js @@ -1,14 +1,15 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; +import wordShift from "../../wordShift"; GameDatabase.celestials.pelle.rifts = { - famine: { + vacuum: { id: 1, - key: "famine", - name: "Famine", + key: "vacuum", + name: ["Vacuum", "Hollow", "Void"], drainResource: "IP", baseEffect: x => `IP gain ${formatX(x, 2, 2)}`, - additionalEffects: () => [PelleRifts.famine.milestones[2]], + additionalEffects: () => [PelleRifts.vacuum.milestones[2]], strike: () => PelleStrikes.infinity, percentage: totalFill => Math.log10(totalFill.plus(1).log10() * 10 + 1) ** 2.5 / 100, percentageToFill: percentage => Decimal.pow(10, @@ -26,33 +27,34 @@ GameDatabase.celestials.pelle.rifts = { galaxyGeneratorThreshold: 1000, milestones: [ { - resource: "famine", + resource: "vacuum", requirement: 0.04, description: "You can equip a single basic Glyph with decreased level and rarity" }, { - resource: "famine", + resource: "vacuum", requirement: 0.06, description: () => `Uncap Replicanti and make its unlock and upgrades ${formatX(1e130)} cheaper`, effect: () => 1e130 }, { - resource: "famine", + resource: "vacuum", requirement: 0.4, - description: "Famine also affects EP gain", - effect: () => Decimal.pow(4, PelleRifts.famine.totalFill.log10() / 2 / 308 + 3), + description: () => `${wordShift.wordCycle(PelleRifts.vacuum.name)} also affects EP gain`, + effect: () => Decimal.pow(4, PelleRifts.vacuum.totalFill.log10() / 2 / 308 + 3), formatEffect: x => `EP gain ${formatX(x, 2, 2)}` }, - ] + ], + galaxyGeneratorText: "There is not enough space left for more, you must fill in the $value" }, - pestilence: { + decay: { id: 2, - key: "pestilence", - name: "Pestilence", + key: "decay", + name: ["Decay", "Collapse", "Disarray"], drainResource: "Replicanti", spendable: true, baseEffect: x => `Replicanti speed ${formatX(x, 2, 2)}`, - additionalEffects: () => [PelleRifts.pestilence.milestones[0], PelleRifts.pestilence.milestones[2]], + additionalEffects: () => [PelleRifts.decay.milestones[0], PelleRifts.decay.milestones[2]], strike: () => PelleStrikes.powerGalaxies, // 0 - 1 percentage: totalFill => totalFill.plus(1).log10() * 0.05 / 100, @@ -64,7 +66,7 @@ GameDatabase.celestials.pelle.rifts = { galaxyGeneratorThreshold: 1e7, milestones: [ { - resource: "pestilence", + resource: "decay", requirement: 0.2, description: "First rebuyable Pelle upgrade also affects 1st Infinity Dimension", effect: () => { @@ -74,14 +76,14 @@ GameDatabase.celestials.pelle.rifts = { formatEffect: x => `1st Infinity Dimension ${formatX(x, 2, 2)}` }, { - resource: "pestilence", + resource: "decay", requirement: 0.6, description: () => `When Replicanti exceeds ${format(DC.E1300)}, all Galaxies are ${formatPercents(0.1)} more effective`, effect: () => (Replicanti.amount.gt(DC.E1300) ? 1.1 : 1) }, { - resource: "pestilence", + resource: "decay", requirement: 1, description: "Increase max Replicanti Galaxies based on total Rift milestones", effect: () => { @@ -90,13 +92,14 @@ GameDatabase.celestials.pelle.rifts = { }, formatEffect: x => `Max RG count +${formatInt(x)}` }, - ] + ], + galaxyGeneratorText: "There's not enough antimatter to form new Galaxies, you need to reverse the $value" }, chaos: { id: 3, key: "chaos", - name: "Chaos", - drainResource: "Pestilence", + name: ["Chaos", "Disorder", "Impurity"], + drainResource: ["Decay", "Collapse", "Disarray"], baseEffect: x => `Time Dimensions ${formatX(x, 2, 2)}`, strike: () => PelleStrikes.eternity, percentage: totalFill => totalFill / 10, @@ -111,11 +114,11 @@ GameDatabase.celestials.pelle.rifts = { }, currency: () => ({ get value() { - return PelleRifts.pestilence.percentage; + return PelleRifts.decay.percentage; }, set value(val) { - const spent = PelleRifts.pestilence.percentage - val; - player.celestials.pelle.rifts.pestilence.percentageSpent += spent; + const spent = PelleRifts.decay.percentage - val; + player.celestials.pelle.rifts.decay.percentageSpent += spent; } }), galaxyGeneratorThreshold: 1e9, @@ -123,7 +126,8 @@ GameDatabase.celestials.pelle.rifts = { { resource: "chaos", requirement: 0.09, - description: "Pestilence effect is always maxed and milestones always active" + description: () => `${wordShift.wordCycle(PelleRifts.decay.name)} \ + effect is always maxed and milestones always active` }, { resource: "chaos", @@ -135,15 +139,16 @@ GameDatabase.celestials.pelle.rifts = { requirement: 1, description: () => `You gain ${formatPercents(0.01)} of your EP gained on Eternity per second`, }, - ] + ], + galaxyGeneratorText: "Your Galaxies are too fragmented, you must stabilize the $value" }, - war: { + recursion: { id: 4, - key: "war", - name: "War", + key: "recursion", + name: ["Recursion", "Dispersion", "Destruction"], drainResource: "EP", baseEffect: x => `EP formula: log(x)/${formatInt(308)} ➜ log(x)/${formatFloat(308 - x.toNumber(), 2)}`, - additionalEffects: () => [PelleRifts.war.milestones[0], PelleRifts.war.milestones[1]], + additionalEffects: () => [PelleRifts.recursion.milestones[0], PelleRifts.recursion.milestones[1]], strike: () => PelleStrikes.ECs, percentage: totalFill => totalFill.plus(1).log10() ** 0.4 / 4000 ** 0.4, percentageToFill: percentage => Decimal.pow(10, percentage ** 2.5 * 4000).minus(1), @@ -152,7 +157,7 @@ GameDatabase.celestials.pelle.rifts = { galaxyGeneratorThreshold: 1e10, milestones: [ { - resource: "war", + resource: "recursion", requirement: 0.10, description: "Dimensional Boosts are more powerful based on EC completions", effect: () => Math.max(100 * EternityChallenges.completions ** 2, 1) * @@ -160,26 +165,27 @@ GameDatabase.celestials.pelle.rifts = { formatEffect: x => `Dimension Boost power ${formatX(x, 2, 2)}` }, { - resource: "war", + resource: "recursion", requirement: 0.15, description: "Infinity Dimensions are stronger based on EC completions", effect: () => Decimal.pow("1e1500", ((EternityChallenges.completions - 25) / 20) ** 1.7).max(1), formatEffect: x => `Infinity Dimensions ${formatX(x)}` }, { - resource: "war", + resource: "recursion", requirement: 1, description: "Unlock the Galaxy Generator", }, - ] + ], + galaxyGeneratorText: "Creating more Galaxies is unsustainable, you must focus the $value to allow more" }, - death: { + paradox: { id: 5, - key: "death", - name: "Death", + key: "paradox", + name: ["Paradox", "Contradiction", "Fallacy"], drainResource: "Dilated Time", baseEffect: x => `All Dimensions ${formatPow(x, 2, 3)}`, - additionalEffects: () => [PelleRifts.death.milestones[2]], + additionalEffects: () => [PelleRifts.paradox.milestones[2]], strike: () => PelleStrikes.dilation, percentage: totalFill => totalFill.plus(1).log10() / 100, percentageToFill: percentage => Decimal.pow10(percentage * 100).minus(1), @@ -188,7 +194,7 @@ GameDatabase.celestials.pelle.rifts = { galaxyGeneratorThreshold: 1e5, milestones: [ { - resource: "death", + resource: "paradox", requirement: 0.15, description: "Time Dimensions 5-8 are much cheaper, unlock more Dilation upgrades", // FIXME: Not a great solution @@ -197,13 +203,13 @@ GameDatabase.celestials.pelle.rifts = { } }, { - resource: "death", + resource: "paradox", requirement: 0.25, - description: () => `Raise Tachyon Particle effect to Dilated Time gain to ${formatPow(1.4, 1, 1)}`, + description: () => `Dilated Time gain becomes Tachyon Particles ${formatPow(1.4, 1, 1)}`, effect: 1.4 }, { - resource: "death", + resource: "paradox", requirement: 0.5, description: "Dilation rebuyable purchase count improves Infinity Power conversion rate", effect: () => Math.min( @@ -212,6 +218,7 @@ GameDatabase.celestials.pelle.rifts = { ), formatEffect: x => `Infinity Power Conversion ${formatX(x, 2, 2)}` }, - ] + ], + galaxyGeneratorText: "It should be possible to create more, but Pelle has restricted you. Disregard the $value" } }; diff --git a/javascripts/core/secret-formula/celestials/singularity-milestones.js b/javascripts/core/secret-formula/celestials/singularity-milestones.js index f0faa7f97..5ce9dcdcd 100644 --- a/javascripts/core/secret-formula/celestials/singularity-milestones.js +++ b/javascripts/core/secret-formula/celestials/singularity-milestones.js @@ -1,4 +1,4 @@ -import { GameDatabase } from "../game-database.js"; +import { GameDatabase } from "../game-database"; // Used for UI purposes to give different theming for different kinds of upgrades export const LAITELA_UPGRADE_DIRECTION = { @@ -55,7 +55,7 @@ GameDatabase.celestials.singularityMilestones = { repeat: 3000, increaseThreshold: 5, limit: Infinity, - description: "You gain more Singularities", + description: "Singularity gain multiplier", effect: completions => Math.pow(2, completions), effectFormat: x => formatX(x, 2, 0), upgradeDirection: LAITELA_UPGRADE_DIRECTION.SELF_BOOST, @@ -105,7 +105,7 @@ GameDatabase.celestials.singularityMilestones = { limit: 4, description: "Dark Matter Dimension Autobuyers", effect: completions => completions, - effectFormat: x => ((x === 0) ? "No autobuyers" : `Autobuy up to DMD ${x}`), + effectFormat: x => ((x === 0) ? "No autobuyers" : `Autobuy up to the ${["1st", "2nd", "3rd", "4th"][x - 1]} DMD`), upgradeDirection: LAITELA_UPGRADE_DIRECTION.SELF_BOOST, }, darkAutobuyerSpeed: { @@ -121,7 +121,7 @@ GameDatabase.celestials.singularityMilestones = { start: 1500, repeat: 10000, limit: 6, - description: "Dark Energy multiplier based on Lai'tela Reality completions", + description: "Dark Energy multiplier based on disabled Dimension count within Lai'tela", effect: completions => Math.pow(1 + 0.05 * completions, Laitela.difficultyTier), effectFormat: x => formatX(x, 2, 2), upgradeDirection: LAITELA_UPGRADE_DIRECTION.SELF_BOOST, @@ -231,7 +231,7 @@ GameDatabase.celestials.singularityMilestones = { start: 8e22, repeat: 0, limit: 1, - description: "Gamespeed boosts Dark Matter and Dark Energy production", + description: "Game speed boosts Dark Matter and Dark Energy production", effect: () => Math.clampMin(Math.log10(getGameSpeedupFactor() / 1e120) / 40, 1), effectFormat: x => formatX(x, 2, 2), upgradeDirection: LAITELA_UPGRADE_DIRECTION.BOOSTS_LAITELA, @@ -240,7 +240,7 @@ GameDatabase.celestials.singularityMilestones = { start: 3e24, repeat: 0, limit: 1, - description: "Singularities boost pre-instability glyph level", + description: "Singularities boost pre-instability Glyph level", effect: () => 1 + Math.clampMin((Math.log10(Currency.singularities.value) - 20) / 30, 0), effectFormat: x => formatX(Math.clampMin(x, 1), 2, 2), upgradeDirection: LAITELA_UPGRADE_DIRECTION.BOOSTS_MAIN, @@ -258,7 +258,7 @@ GameDatabase.celestials.singularityMilestones = { start: 3e38, repeat: 0, limit: 1, - description: "Infinities gain power effect based on singularities", + description: "Infinities gain a power effect based on Singularities", effect: () => 1 + Math.log10(Currency.singularities.value + 1) / 300, effectFormat: x => formatPow(x, 2, 3), upgradeDirection: LAITELA_UPGRADE_DIRECTION.BOOSTS_MAIN, diff --git a/javascripts/core/secret-formula/celestials/strikes.js b/javascripts/core/secret-formula/celestials/strikes.js index 5650118b1..5618efd93 100644 --- a/javascripts/core/secret-formula/celestials/strikes.js +++ b/javascripts/core/secret-formula/celestials/strikes.js @@ -1,40 +1,44 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; +import wordShift from "../../wordShift"; GameDatabase.celestials.pelle.strikes = { infinity: { id: 1, requirementDescription: "Reach Infinity", penaltyDescription: () => `Antimatter Dimensions are raised to ${formatPow(0.5, 1, 1)}`, - rewardDescription: "Unlock Famine", - rift: () => PelleRifts.famine + rewardDescription: () => `Unlock ${wordShift.wordCycle(PelleRifts.vacuum.name)} + and get a permanent Infinity Autobuyer`, + rift: () => PelleRifts.vacuum }, powerGalaxies: { id: 2, requirementDescription: "Power-up Galaxies", penaltyDescription: () => `Infinity Dimensions are raised to ${formatPow(0.5, 1, 1)}`, - rewardDescription: "Unlock Pestilence", - rift: () => PelleRifts.pestilence + rewardDescription: () => `Unlock ${wordShift.wordCycle(PelleRifts.decay.name)}`, + rift: () => PelleRifts.decay }, eternity: { id: 3, requirementDescription: "Reach Eternity", penaltyDescription: () => `Replicanti speed scales harsher after ${format(DC.E2000)}`, - rewardDescription: "Unlock Chaos", + rewardDescription: () => `Unlock ${wordShift.wordCycle(PelleRifts.chaos.name)}`, rift: () => PelleRifts.chaos }, ECs: { id: 4, requirementDescription: () => `Reach ${formatInt(115)} TT`, - penaltyDescription: "Famine IP multiplier is reduced in Eternity Challenges", - rewardDescription: "Unlock War", - rift: () => PelleRifts.war + penaltyDescription: () => + `${wordShift.wordCycle(PelleRifts.vacuum.name)} IP multiplier is reduced in Eternity Challenges`, + rewardDescription: () => `Unlock ${wordShift.wordCycle(PelleRifts.recursion.name)}`, + rift: () => PelleRifts.recursion }, dilation: { id: 5, requirementDescription: "Dilate Time", - penaltyDescription: "Time Dilation nerfs are always active", - rewardDescription: "Unlock Death", - rift: () => PelleRifts.death + penaltyDescription: "Time Dilation is permanently active", + rewardDescription: () => `Keep access to Time Dilation upgrades across Armageddon and unlock + ${wordShift.wordCycle(PelleRifts.paradox.name)}`, + rift: () => PelleRifts.paradox } }; diff --git a/javascripts/core/secret-formula/celestials/teresa.js b/javascripts/core/secret-formula/celestials/teresa.js new file mode 100644 index 000000000..c31897779 --- /dev/null +++ b/javascripts/core/secret-formula/celestials/teresa.js @@ -0,0 +1,41 @@ +import { GameDatabase } from "../game-database"; + +GameDatabase.celestials.teresa = { + unlocks: { + run: { + id: 0, + price: 1e14, + description: "Unlock Teresa's Reality.", + onUnlock: () => Teresa.quotes.unlockReality.show(), + }, + epGen: { + id: 1, + price: 1e18, + description: "Unlock passive Eternity Point generation.", + isDisabledInDoomed: true + }, + effarig: { + id: 2, + price: 1e21, + description: "Unlock Effarig, Celestial of Ancient Relics.", + onUnlock: () => Teresa.quotes.effarig.show(), + }, + shop: { + id: 3, + price: 1e24, + description: "Unlock the Perk Point Shop.", + }, + undo: { + id: 4, + price: 1e10, + description: "Unlock \"Undo\" of equipping a Glyph.", + isDisabledInDoomed: true + }, + startEU: { + id: 5, + price: 1e6, + description: "You start Reality with all Eternity Upgrades unlocked.", + isDisabledInDoomed: true + } + } +}; diff --git a/javascripts/core/secret-formula/celestials/v.js b/javascripts/core/secret-formula/celestials/v.js index 6a0baf154..9a3bca1b5 100644 --- a/javascripts/core/secret-formula/celestials/v.js +++ b/javascripts/core/secret-formula/celestials/v.js @@ -1,5 +1,6 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; + // This is supposed to be in ./navigation.js but importing doesn't work for some stupid reason function emphasizeEnd(fraction) { return Math.pow(fraction, 10); @@ -195,4 +196,61 @@ GameDatabase.celestials.v = { isHard: true } ], + unlocks: { + vAchievementUnlock: { + id: 0, + reward: "Unlock V, The Celestial Of Achievements", + description: "Meet all the above requirements simultaneously", + requirement: () => Object.values(GameDatabase.celestials.v.mainUnlock).every(e => e.progress() >= 1) + }, + shardReduction: { + id: 1, + reward: `You can spend Perk Points to reduce the goal requirement of all tiers of each V-Achievement.`, + description: () => `Have ${formatInt(2)} V-Achievements`, + requirement: () => V.spaceTheorems >= 2 + }, + adPow: { + id: 2, + reward: "Antimatter Dimension power based on total Space Theorems.", + description: () => `Have ${formatInt(5)} V-Achievements`, + effect: () => 1 + Math.sqrt(V.spaceTheorems) / 100, + format: x => formatPow(x, 3, 3), + requirement: () => V.spaceTheorems >= 5 + }, + fastAutoEC: { + id: 3, + reward: "Achievement multiplier reduces Auto-EC completion time.", + description: () => `Have ${formatInt(10)} V-Achievements`, + effect: () => Achievements.power, + // Base rate is 60 ECs at 20 minutes each + format: x => (Ra.unlocks.instantECAndRealityUpgradeAutobuyers.canBeApplied + ? "Instant (Ra upgrade)" + : `${TimeSpan.fromMinutes(60 * 20 / x).toStringShort()} for full completion`), + requirement: () => V.spaceTheorems >= 10 + }, + autoAutoClean: { + id: 4, + reward: "Unlock the ability to Automatically Purge on Reality.", + description: () => `Have ${formatInt(16)} V-Achievements`, + requirement: () => V.spaceTheorems >= 16 + }, + achievementBH: { + id: 5, + reward: "Achievement multiplier affects Black Hole power.", + description: () => `Have ${formatInt(30)} V-Achievements`, + effect: () => Achievements.power, + format: x => formatX(x, 2, 0), + requirement: () => V.spaceTheorems >= 30 + }, + raUnlock: { + id: 6, + reward() { + return `Reduce the Space Theorem cost of Time Studies by ${formatInt(2)}. + Unlock Ra, Celestial of the Forgotten.`; + }, + description: () => `Have ${formatInt(36)} V-Achievements`, + effect: 2, + requirement: () => V.spaceTheorems >= 36 + } + } }; diff --git a/javascripts/core/secret-formula/challenges/eternity-challenges.js b/javascripts/core/secret-formula/challenges/eternity-challenges.js index 79b5dc2cb..7e78a735a 100644 --- a/javascripts/core/secret-formula/challenges/eternity-challenges.js +++ b/javascripts/core/secret-formula/challenges/eternity-challenges.js @@ -1,5 +1,5 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; const specialInfinityGlyphDisabledEffectText = () => (PelleRifts.chaos.milestones[1].canBeApplied ? "The Pelle-Specific effect from Infinity Glyphs is also disabled." @@ -186,7 +186,7 @@ GameDatabase.challenges.eternity = [ { id: 12, description: () => (PlayerProgress.realityUnlocked() - ? `the game runs ×${formatInt(1000)} slower; all other gamespeed effects are disabled. The goal must be reached + ? `the game runs ×${formatInt(1000)} slower; all other game speed effects are disabled. The goal must be reached within a certain amount of time or you will fail the Challenge. ${specialInfinityGlyphDisabledEffectText()}` : `the game runs ×${formatInt(1000)} slower. The goal must be reached within a certain amount of time or you will fail the Challenge.`), diff --git a/javascripts/core/secret-formula/challenges/infinity-challenges.js b/javascripts/core/secret-formula/challenges/infinity-challenges.js index d59b8938e..6be72f669 100644 --- a/javascripts/core/secret-formula/challenges/infinity-challenges.js +++ b/javascripts/core/secret-formula/challenges/infinity-challenges.js @@ -1,11 +1,11 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; GameDatabase.challenges.infinity = [ { id: 1, description: `all Normal Challenges, with the exception of - Tickspeed (C9) and Big Crunch (C11) Challenges, are active at the same time.`, + Tickspeed (C9) and Big Crunch (C12) Challenges, are active at the same time.`, goal: DC.E650, isQuickResettable: true, reward: { diff --git a/javascripts/core/secret-formula/challenges/normal-challenges.js b/javascripts/core/secret-formula/challenges/normal-challenges.js index daafa2ffa..cd4288380 100644 --- a/javascripts/core/secret-formula/challenges/normal-challenges.js +++ b/javascripts/core/secret-formula/challenges/normal-challenges.js @@ -1,5 +1,5 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; // I tried to make it relatively simple to add more locks; the idea is that you give it a value here // and then it's all handled in the backend @@ -19,6 +19,7 @@ GameDatabase.challenges.normal = [ name: "1st Antimatter Dimension Autobuyer", reward: "Upgradeable 1st Antimatter Dimension Autobuyer", lockedAt: DC.D0, + isDisabledInDoomed: true, }, { id: 2, @@ -30,6 +31,7 @@ GameDatabase.challenges.normal = [ name: "2nd Antimatter Dimension Autobuyer", reward: "Upgradeable 2nd Antimatter Dimension Autobuyer", lockedAt: DC.D0, + isDisabledInDoomed: true, }, { id: 3, @@ -41,6 +43,7 @@ GameDatabase.challenges.normal = [ name: "3rd Antimatter Dimension", reward: "Upgradeable 3rd Antimatter Dimension Autobuyer", lockedAt: DC.D0, + isDisabledInDoomed: true, }, { id: 4, @@ -51,6 +54,7 @@ GameDatabase.challenges.normal = [ name: "4th Antimatter Dimension Autobuyer", reward: "Upgradeable 4th Antimatter Dimension Autobuyer", lockedAt: DC.D0, + isDisabledInDoomed: true, }, { id: 5, @@ -61,6 +65,7 @@ GameDatabase.challenges.normal = [ name: "5th Antimatter Dimension Autobuyer", reward: "Upgradeable 5th Antimatter Dimension Autobuyer", lockedAt: DC.D0, + isDisabledInDoomed: true, }, { id: 6, @@ -71,6 +76,7 @@ GameDatabase.challenges.normal = [ name: "6th Antimatter Dimension Autobuyer", reward: "Upgradeable 6th Antimatter Dimension Autobuyer", lockedAt: DC.D0, + isDisabledInDoomed: true, }, { id: 7, @@ -82,6 +88,7 @@ GameDatabase.challenges.normal = [ name: "7th Antimatter Dimension Autobuyer", reward: "Upgradeable 7th Antimatter Dimension Autobuyer", lockedAt: DC.D0, + isDisabledInDoomed: true, }, { id: 8, @@ -92,6 +99,7 @@ GameDatabase.challenges.normal = [ name: "8th Antimatter Dimension Autobuyer", reward: "Upgradeable 8th Antimatter Dimension Autobuyer", lockedAt: DC.D0, + isDisabledInDoomed: true, }, { id: 9, @@ -102,6 +110,7 @@ GameDatabase.challenges.normal = [ name: "Tickspeed Autobuyer", reward: "Upgradeable Tickspeed Autobuyer", lockedAt: DC.D0, + isDisabledInDoomed: true, }, { id: 10, @@ -112,6 +121,7 @@ GameDatabase.challenges.normal = [ name: "Automated Dimension Boosts", reward: "Dimension Boosts Autobuyer", lockedAt: DC.D16, + isDisabledInDoomed: true, }, { id: 11, @@ -122,6 +132,7 @@ GameDatabase.challenges.normal = [ name: "Automated Antimatter Galaxies", reward: "Antimatter Galaxies Autobuyer", lockedAt: DC.D16, + isDisabledInDoomed: true, }, { id: 12, diff --git a/javascripts/core/secret-formula/changelog.js b/javascripts/core/secret-formula/changelog.js new file mode 100644 index 000000000..5418c8c5e --- /dev/null +++ b/javascripts/core/secret-formula/changelog.js @@ -0,0 +1,472 @@ +import { GameDatabase } from "./game-database"; + +GameDatabase.changelog = [ + /** + * @template + * { + * @property {Array: Number} date Date of the release of the update, stored in order of year-month-date. + * @property {String} name Name of the update entry. Optional. + * @property {Number} id Unique ID for each entry (generated in-game, not explicitly stated) + * @property {function: @return String} info Text body of information for the entry. + * } + */ + { + date: [2018, 6, 17], + name: "This Update Sucks", + info: ` +MAJOR STUFF:
+
    +
  • TIME DILATION
  • +
  • 3 ROWS OF SECRET ACHIEVEMENTS
  • +
  • Added more Nicholas Cage.
  • +
  • 1 new row of achievements.
  • +
  • Added 3 study tree save slots.
  • +
  • Greatly improved performance. (up to 5x in certain cases, ~3x in almost all cases)
  • +
  • Nerfed EC10 reward. ((infinities * EC10 completions * 0.000002+1) > +(infinities ^ 0.9 * EC10 completions * 0.000002+1))
  • +
  • Added even more Nicholas Cage.
  • +
  • Time study 11 has been capped at 1e2500 and now displays its current multiplier.
  • +
  • Time study 193 has been buffed, requires ~1012680 eternities to cap, rather than 1.5m, and is now capped at 1e13000 +instead of ~1.81e12900/1.5m eternities. (1.02^x) > (1.03^x)
  • +
  • The second eternity upgrade has been buffed, and now soft caps at 100k, rather than 125k. The end result is very +slightly higher. ((x/300)^log4(2x)) > ((x/200)^log4(2x))
  • +
  • EC1 now requires 20k eternities per tier to unlock, down from 25k.
  • +
  • TD cost scaling has been increased after costs of 1e1300. (this is in addition to the current increase)
  • +
  • Added additional galaxy cost scaling after 800 galaxies.
  • +
  • Added a button to buy the maximum amount of eternity point multipliers at once.
  • +
  • Offline progress processes ~5x faster, and now simulates autobuyers. (please note that offline progress is still +capped at 1000 ticks, with additional ticks increasing the production of said 1000 ticks)
  • +
  • Added a new save file system that allows 3 different save files at once all with cloud save enabled, along with +a new cloud save UI.
  • +
  • Added an animation to visualize your multiplier gain when you purchase 10 of a dimension, dimension boost/shift, +or sacrifice.
  • +
  • Nicholas Cage.
  • +
  • Added an animation to big crunches. This will only trigger if you haven't eternitied, have a fastest infinity time +above 1 minute, and haven't broken infinity.
  • +
  • Added a button in the options menu to disable individual animations.
  • +
  • Added more news ticker entries
  • +
+
+Minor stuff:
+
    +
  • Reduced the space between the secondary eternity tab buttons.
  • +
  • The EC3 description now specifies that dimensional sacrifice is disabled.
  • +
  • Autobuyer inputs now support commas and notation on exponents.
  • +
  • When purchasing the EP or IP multipliers, autobuyer inputs will now always format the updated value above 1000.
  • +
  • The size and placement of the auto IP multiplier and auto RG toggles have been adjusted to fit with the other auto +toggles.
  • +
  • Total time played now increases at a normal rate inside EC12.
  • +
  • Fastest infinity time now updates normally inside EC12.
  • +
  • The time theorem purchasing background is now 20 pixels wider.
  • +
  • Changed the wording on time study 133 for clarity.
  • +
  • Added various missing periods to achievements.
  • +
  • Improved chart performance. (it's still pretty laggy if your settings are too high)
  • +
  • You can now purchase study 201 while you have EC11/12 bought, but you cannot purchase another path.
  • +
  • Purchasing study 131 no longer turns off your replicanti galaxy autobuyer, but instead displays it as disabled.
  • +
  • You can now purchase another split using shift while you have study 201.
  • +
  • You now purchase max galaxies manually by clicking or using the hotkey with more than 6 eternities.
  • +
  • You can now purchase single dimension boosts and galaxies by holding shift while purchasing.
  • +
  • ID8 will now display a rate of change after completing EC7 at least once.
  • +
  • Added an oxford comma to formatted time values.
  • +
  • Made the dimensional sacrifice button 40px wider to prevent the text overflowing.
  • +
  • Made the all tab eternity and infinity point displays 30px wider to prevent the text overflowing.
  • +
  • Moved the big crunch button up to prevent blocking the statistics and achievement tab buttons.
  • +
  • Moved the eternity and infinity buttons inwards to prevent the HTML layout jumping around.
  • +
  • Fixed the placement of certain footers.
  • +
  • Fixed a typo where a news ticker said "Dimesional Sacrifice" instead of "Dimensional Sacrifice"
  • +
  • Fixed a bug where TDs displayed a 2x multiplier per purchase when they actually gave a 4x multiplier.
  • +
  • Fixed a bug where study 51 wouldn't respect notation.
  • +
  • Fixed a bug where the infinity challenges tab would always show.
  • +
  • Fixed a bug where the auto RG toggle would jiggle left and right 1 pixel in certain cases.
  • +
  • Fixed a bug where the rate of change on the 7th dimension wouldn't take into account ID1 while in EC7.
  • +
  • Fixed a bug where EC12 displayed 0.1 seconds after 5 completions, but actually required 0.0 seconds.
  • +
  • Fixed a bug where tickspeed elements wouldn't hide correctly in certain cases.
  • +
  • Fixed a bug where bought eternity challenge unlock studies would show as gray in the dark theme rather than a deep +purple.
  • +
  • Fixed a bug where dimensions 5-8 would hide upon eternity even with the 30 eternity milestone.
  • +
  • Fixed a bug where popup colors weren't inverted in the inverted and inverted metro themes.
  • +
  • Fixed a bug where the eternity point amount wouldn't show when you imported a save with eternity points into a save +without them.
  • +
  • Fixed a bug where locked eternity challenges didn't have a hover effect in the dark metro theme.
  • +
  • Fixed a bug where popups weren't properly centered.
  • +
  • Fixed a bug where ID autobuyers would purchase IDs upon unlock even while disabled.
  • +
  • Fixed a bug where study tree branches drawn to row 22 were off-centered.
  • +
  • Fixed a bug where EP/min and IP/min peaks wouldn't update properly upon import.
  • +
  • Fixed a bug where infinity dimension autobuyers wouldn't hide properly upon import.
  • +
  • Fixed a bug where the IP multiplier autobuyer wouldn't hide properly upon import.
  • +
  • Fixed a bug where the option to change big crunch modes wouldn't hide properly upon import.
  • +
  • Fixed a bug where the max buy galaxy interval setting wouldn't hide properly upon import.
  • +
  • Fixed a bug where the RG autobuyer wouldn't hide properly upon import.
  • +
  • Fixed a bug where the eternity confirmation option wouldn't hide properly upon import.
  • +
  • Fixed a bug where the replicanti upgrade autobuyers wouldn't hide properly upon import.
  • +
  • Fixed a bug where your update rate wouldn't update upon import.
  • +
  • Fixed a bug where the chart line color wouldn't update properly upon import.
  • +
  • Fixed a bug where achievement images were being cut off by 4 pixels on the right and bottom sides.
  • +
  • Fixed a bug where "Yo dawg, I heard you liked infinities..." only required 1e300 times the previous infinity.
  • +
  • Fixed a bug where the auto sacrifice interval would still display as 0.10 seconds even with the +double autobuyer speed breaking infinity upgrade.
  • +
  • Fixed a bug where certain time studies were 1 pixel too far to the left or right.
  • +
  • Fixed a bug where studies 223 & 224 weren't taken into account when displaying antimatter galaxies as +distant antimatter galaxies.
  • +
  • Fixed a bug where study 227 would multiply your 4th time dimension production by 0 +if you had no sacrifice bonus.
  • +
  • Fixed a bug where the game would say "You have 1 eternity points." rather than "You have 1 eternity point.".
  • +
  • Fixed a bug where popups would remain open after changing tabs.
  • +
  • Fixed a bug where you were able to select the achievement images by clicking and dragging over them.
  • +
  • Fixed a bug where studies 233 and 234 had the wrong classes assigned to them on load.
  • +
+` + }, + { + date: [2018, 4, 1], + name: "Fixed a Bug where there wasn't an Update", + info: ` +Huge thanks to Omsi for helping me a ton with this.

+MAJOR STUFF:
+
    +
  • 2 NEW ETERNITY CHALLENGES
  • +
  • 12 NEW TIME STUDIES
  • +
  • Time study 132 has been buffed from a 30% bonus to a 40% bonus.
  • +
  • Added an achievement bonus for "Popular music": "Replicanti galaxies divide your replicanti by 1.79e308 instead of +resetting them to 1."
  • +
  • Added an achievement bonus for "IT'S OVER 9000": "Sacrifice doesn't reset your dimensions."
  • +
  • Added an achievement bonus for "Like feasting on a behind": "IP multiplier based on time spent this infinity."
  • +
  • Added an achievement bonus for "What do I have to do to get rid of you": "Time dimensions are multiplied by +the number of studies you have."
  • +
  • Added "Infinity" notation.
  • +
  • Added "Brackets" notation.
  • +
  • Added an import/export system for the time study tree.
  • +
  • Added an EP/min & peak EP/min display to the eternity button.
  • +
  • Added an eternity hotkey.
  • +
  • Added something to help you pick your theme.
  • +
  • Added a few more IAPs.
  • +
  • Reduced the cost of "Double IP gain from all sources" IAP from 50 ➜ 40
  • +
+
+Minor stuff:
+
    +
  • Added an option to not plot drops in production on the chart. (It will instead copy the newest data point)
  • +
  • Added displays for the current bonuses from time studies 71, 72, and 73.
  • +
  • Built up speed for 6 hours to do it in 0.5x A presses.
  • +
  • Changed study 72 to only work on the 4th infinity dimension, but doubled its power. (No effective change)
  • +
  • Alchemy 120 (Vivification) scaling decreased.
  • +
  • Fixed a bug where the buttons to purchase time studies wouldn't move in inverted themes on firefox.
  • +
  • Fixed a bug. Antman, you're good to go.
  • +
  • Fixed a bug that gave you the ability to set a custom name for your theme when using a secret theme.
  • +
  • Fixed BLJ. Shoutout to SimpleFlips.
  • +
  • Fixed a bug that caused purchasing the EP multiplier to require multiple clicks.
  • +
  • Removed the ghost from the game. Was annoying.
  • +
  • Fixed a bug that allowed you to earn "Long lasting relationship" in EC7.
  • +
  • Monkeys no longer eat humans, as intended.
  • +
  • Fixed a bug where the reward from EC7 could display -1.
  • +
  • Increased the drop rate of collector's pendant items by 20%.
  • +
  • Fixed a bug where the infinity requirement for EC4 could be less than 0.
  • +
  • Transcension gives less Ancient Souls.
  • +
  • Fixed a bug where the visual display for autobuyer bulk buy settings wouldn't update upon your first eternity.
  • +
  • Fixed the rickroll. Now it's properly not working.
  • +
  • Fixed a bug where the EP multiplier would break if its power exceeded 1.79e308.
  • +
  • Leeroy Jenkins' Battlecry now doesn't trigger Patches.
  • +
  • Fixed a bug where the confirmation for starting an infinity challenge would say you need to reach infinity.
  • +
  • Cursors now do circles around the cookie.
  • +
  • Fixed a bug where the offline progress popup would simply say "While you were away" if nothing happened.
  • +
  • Traction has been slightly increased to reduce unwanted drifts.
  • +
  • Fixed a bug that in rare cases would cause the offline progress popup to say you gained "NaNeInfinity" time shards +or infinity power.
  • +
  • Fixed a bug where the tickspeed visual display wouldn't update upon any form of reset.
  • +
  • Bugged a fix where eternity was dumb.
  • +
  • CS now makes notes go faster in mania.
  • +
  • Fixed a bug where replicanti were hidden but still unlocked if you eternitied for the 50th time +while they were locked.
  • +
  • Dirt is now more abundant.
  • +
  • Fixed a bug where the 1st dimension wasn't producing the 0th dimension.
  • +
  • Fixed a bug where The Nameless Ones were too easy.
  • +
  • Fixed a bug where in a specific case, 2 eternity challenges would appear as running at the same time.
  • +
  • Increased TukkunFCG YC rewards by 15%.
  • +
  • Added more space. SPAAAAAAACE
  • +
  • Fixed a bug where the eternity challenges tab would hide after refreshing with less than 1e2000 antimatter.
  • +
  • Fixed a bug where eternity challenges wouldn't update correctly upon import.
  • +
  • Fixed a bug where dimension display values wouldn't update in certain cases.
  • +
  • Portals are now not red.
  • +
  • Fixed a bug where the ON/OFF text on the challenge confirmation option wasn't capitalized upon load.
  • +
  • Reduced GRB's autokill threshold to 2500/2000 power/toughness.
  • +
  • Fixed a typo where the eternity confirmation option said "Eternity confimation".
  • +
  • Added bugs because Omsi wants more bugs to fix. Absolute legend, I'm telling you, the queen is legendary.
  • +
  • Fixed a typo where the reward for "That's faster!" said you started with 20000 antimatter, rather than 200000.
  • +
  • Added depression to your themes.
  • +
  • Fixed inconsistencies with the standard notation naming convention.
  • +
  • Tried to fix a bug where the game was bad but failed. The game is still bad.
  • +
  • Changed the wording on EC4 to say "X or less" rather than "less than X". +(It always worked this way, this is just a correction)
  • +
  • Made donkeys less fast, so you can actually catch them now.
  • +
  • Changed the wording on the EC2 reward to say "affects 1st Infinity Dimension" rather than +"affects Infinity Dimensions". (It always worked this way, this is just a correction)
  • +
  • Increased the base breeding speed of trimps by 10%.
  • +
  • You can now click through the footer and progress bar to access buttons that they are overlapping. +(This is for smaller screens)
  • +
  • Made periods longer.
  • +
  • Added loot boxes.
  • +
  • Removed loot boxes.
  • +
  • Added various missing periods to achievement descriptions.
  • +
  • Added a missing period to time spent in this eternity.
  • +Increased the price of creation count increases from 50 god power to 60. +
  • Added a missing space to the "Autobuyers work twice as fast." upgrade.
  • +
  • Manually buying max dimension boosts no longer requires 10 eternities or more, and now only requires the bulk buy +dimension boosts breaking infinity upgrade.
  • +
  • Did a barrel roll.
  • +
  • Added more useless patch notes
  • +
+` + }, + { + date: [2018, 2, 1], + name: "Eternity Challenges", + info: ` +
    +
  • NEW TIME STUDIES
  • +
  • 2 new achievement rows
  • +
  • Made certain news messages only show if you have reached certain levels of progression
  • +
  • Massively improved performance of calculating dimension costs thanks to SpectralFlame. +(Cuts cpu usage by up to 2/3 in late-game)
  • +
  • New news (get it?) ticker entries.
  • +
  • Added a production chart.
  • +
  • Added new statistics to replace the scale statistic after 1e100000 antimatter.
  • +
  • Added a new milestone for 30 eternities: "Start with all normal dimensions available for purchase".
  • +
  • Added an option to change the update rate of the game, ranging from 33ms to 200ms. +(before this, it was locked at 50ms)
  • +
  • The game now partially simulates offline progress, instead of estimating it.
  • +
  • Added 3 new eternity upgrades.
  • +
  • Added a reward to the "NEW DIMENSIONS???" achievement, "Your achievement bonus affects Infinity Dimensions."
  • +
  • Buffed time study 111. (10 ^ (log10(antimatter) / 290- 0.75)) > (10 ^ (log10(antimatter) / 285- 0.75))
  • +
  • Buffed time study 83. (1.0001^x) > (1.0004^x)
  • +
  • Nerfed eternity upgrade 1. ((x+1)^3) > (x+1)
  • +
  • Nerfed eternity upgrade 2. (x^log4(2x)) > ((x/300)^log4(2x) with harsher formula above 125,000)
  • +
  • Fixed a bunch of bugs and changed a bunch of things. (more detail below)
  • +
  • Added buy max buttons to Time Dimensions and Time Theorems.
  • +
  • Added a hotkey for replicanti galaxies. (R)

  • +
  • Nitty gritty:
  • +
  • Greatly improved the performance of calculating bonus tickspeed from time dimensions.
  • +
  • Replaced all references to soft resets with references to dimension boosts.
  • +
  • Made achievements update on import/hardreset.
  • +
  • Made the game take into account your infinity points gained on crunch for the purposes of +eternity point gain when you eternity.
  • +
  • The replicanti interval is now displayed after and reductions / increases.
  • +
  • Added missing periods to various achievements.
  • +
  • Made the bonus from time study 131 display next to max replicanti galaxies.
  • +
  • Added time dimensions to the info scale.
  • +
  • Changed the description of time study 31 to "Powers up bonuses that are based on your infinitied stat +(to the power of 4)" from "Powers up existing upgrades based on infinitied stat (to the power of 4)".
  • +
  • Changed the description of "MAXIMUM OVERDRIVE" to say "Big Crunch with X" instead of "Reach X".
  • +
  • Added "with reduced effect" to the description of time study 71, 72, and 73.
  • +
  • Changed the text on autobuy max dimension boosts to "Buy max dimboosts every X seconds:" +from "Max dimboost interval:". (To achieve parity with the autobuy max galaxies text)
  • +
  • Made the challenges button always show if you have more than 1 eternity.
  • +
  • Fixed centering issues with infinity and eternity upgrades.
  • +
  • Various minor changes to themes to improve consistency. (Too minute to list, even here)
  • +
  • Made the eternity autobuyer number multiply by 5 when you buy the eternity point multiplier.
  • +
  • Increased the requirement for "Is This Hell?". (5 > 6.66 seconds)
  • +
  • Reduced the starting replicanti interval upgrade cost. (1e160 > 1e140)
  • +
  • Galaxies are labeled "Distant Antimatter Galaxies" when the cost scaling starts. (At 100 galaxies)
  • +
  • Dimensions no longer produce anything after reaching challenge goal, or after reaching infinity with fixed infinity. +This is due to the c6 being abusable.
  • +
  • Made the 7 and 25 eternity milestones work much faster.
  • +
  • After unlocking bulk dimboosts, clicking dimension boost or pressing D will buy max dimension boosts.
  • +
  • Moved fake news, don't you dare to sleep, spreading cancer, and one for each dimension to rows 2, 3, 4, +and 7 respectively.
  • +
  • Added a visual display of how many galaxies/dim boosts you have next to the cost.
  • +
  • Added an explanation of hotkeys to the options page.
  • +
  • Made shift+1-8 purchase singular dimensions and shift+T purchase a singular tickspeed upgrade.
  • +
  • Reworked the display of the buy time theorem buttons.
  • +
  • The milestones page now has 2 columns.
  • +
  • Extended support for standard notation to e3e18, and letter/cancer notation (almost) infinitely.
  • +
  • Added support for standard, letter and logrithm notation in autobuyer inputs.
  • +
  • Added "in a challenge" to the description of "Zero Deaths".
  • +
  • Made most large numbers in achievements be listed in your chosen notation.
  • +
  • Nerfed "Gift From The Gods"'s achievement reward.
  • +
  • Made purchasing time theorems with EP require at least 1 time dimension.
  • +
  • First eternity now takes you to the time dimensions tab.
  • +
  • Time dimension prices now have 2 decimal places.
  • +
  • Reformatted the tick interval reduction text for very small numbers.
  • +
  • The game now keeps track of when you automatically do an infinity, and you can passively gain IP based off the +IP/min in that run if you go offline (but only if infinity isn't broken).
  • +
  • Made time study 171 apply retroactively. This was causing an issue with production being much lower than expected +when going into a long run on the same run as respeccing.
  • +
  • Fixed a bug where max all wasn't giving achievements when buying dimensions.
  • +
  • Fixed a bug where the game wouldn't show the default dimensions tab upon hard resetting.
  • +
  • Fixed a bug where time dimensions were called "X Dimension" rather than "X Time Dimension".
  • +
  • Fixed a bug where the replicanti galaxy button would show as locked if you had more than +the listed max replicanti galaxies and study 131.
  • +
  • Fixed a bug where the last ten eternities average said IP/X rather than EP/X.
  • +
  • Fixed a bug where the big crunch autobuyer said "X times since last crunch" instead of "X times last crunch".
  • +
  • Fixed a bug where the challenge records display wouldn't update upon import.
  • +
  • Fixed a bug where hotkeys wouldn't work sometimes.
  • +
  • Fixed a bug where secret theme names would display as "0" after refreshing.
  • +
  • Fixed a bug where time studies would move around when your window size was too small.
  • +
  • Fixed a bug where infinity dimensions would reset when clicking on a challenge and not entering while +challenge confirmations were on.
  • +
  • Fixed a bug where you always had the infinity challenge 1 reward.
  • +
  • Fixed a bug where eternity milestone classes weren't set correctly upon import.
  • +
  • Fixed a bug where the eternity autobuyer, sacrifice autobuyer, time dimension tab, and replicanti +wasn't hiding correctly upon import.
  • +
  • Fixed a bug where buy max dim boosts was able to buy 1 too many boosts.
  • +
  • Fixed a bug where the study tree would be off-centered if the game windows wasn't wide enough.
  • +
  • Fixed a bug where you could buy factions of dimension boosts with dimension boost bulk buy.
  • +
  • Fixed a bug where your autobuy max dimension boost interval would set itself to itself +if you eternitied while changing it.
  • +
  • Fixed a bug where secondary statistic tabs weren't hiding upon import.
  • +
  • Fixed a bug where replicanti galaxies wouldn't give a bonus if you had less than 3 galaxies.
  • +
  • Fixed a bug where the dimension boost autobuyer would ignore dimension boost costs until they costed 8th dimensions. +
  • +
  • Fixed a bug where the future shop multipliers were displayed before the x rather than after.
  • +
  • Fixed a bug where the challenge confirmation button's off and on were lowercase.
  • +
  • Fixed a bug where the static infinity point display would disappear after eternity.
  • +
` + }, + { + date: [2017, 12, 1], + name: "\"Eternity\" update", + info: ` +
    +
  • Time studies tree with free respec
  • +
  • Eternity Milestones with tons of automation
  • +
  • Eternity upgrades
  • +
  • TIME DIMENSIONS
  • +
  • REPLICANTIS
  • +
  • More themes made by Omsi
  • +
  • Disable hotkeys option
  • +
  • Current IP/min post-break
  • +
  • Infinity Challenge times
  • +
  • Past 10 eternities
  • +
  • Lowered IP multiplier cost by 1 Order of magnitude.
  • +
  • 3 more rows of achievements
  • +
  • Infinity challenge reward nerfs (1st: 1.5x ➜ 1.3x; 3rd: lowered; 4th: mult^1.1 ➜ mult^1.05)
  • +
  • More news ticker entries
  • +
  • Immensely improved performance thanks to break_infinity.js made by Patashu, it replaces decimal.js
  • +
  • Added LZString for cloud saving purposes.
  • +
  • Achievement refractoring to reduce save string size made by StrangeTim.
  • +
  • Commas between exponents option for numbers higher than e100000
  • +
  • Added logarithm notation
  • +
  • Made letter and cancer notation last longer. +
+` + }, + { + date: [2017, 10, 10], + info: ` +
    +
  • Complete refactoring for all upgrade UI.
  • +
  • Minor Upgrade Changes. (Capping some upgrades)
  • +
  • Kred shop- 3 paid Upgrades- More upgrades (and upgrade improvements) coming in the future.
  • +
  • 8 new Achievements- Achievement Rewards have also been added.
  • +
  • Infinity Challenges- additional challenges to do going from Inf Dim 2 to current end game and beyond.
  • +
  • Main Screen UI updates- IP points are now visible everywhere.
  • +
  • Hotkeys- C for Big Crunch, M for Max All, S for Dimensional Sacrifice, D for Dimension Shift/Boost, +G for Antimatter Galaxy, Numbers 1-8 for Buy 10 (D1-8), A for Toggle Autobuyers.
  • +
  • Bug Fixes- At least 2, including a percentage buff.
  • +
` + }, + { + date: [2017, 9, 25], + info: ` +
    +
  • NEW DIMENSIONS?
  • +
  • Super Secret Post-Infinity Dimensions added. Get more antimatter to find out!
  • +
  • Post-break double galaxy upgrade nerfed. It now gives 50% more.
  • +
  • Four new post-break upgrades added.
  • +
  • Scaling of the dimension cost multiplier increased.
  • +
  • Eight new achievements added.
  • +
  • Cloud saving maybe added.
  • +
  • Refunded Dimension cost increase multiplier and changed the cost.
  • +
` + }, + { + date: [2017, 9, 19], + name: "Breaking Infinity", + info: ` +
    +
  • Post infinity content added (Breaking infinity), requires big crunch speed to be maxed.
  • +
  • New upgrade tree pre-breaking, included one upgrade that be taken multiple times to +increase infinity point gains.
  • +
  • Eight late game post-breaking upgrades.
  • +
  • Eight new achievements.
  • +
  • Reworked autobuyer prices and times, full refund for all points spent on them.
  • +
  • Autobuyers now can be upgraded beyond 0.1 seconds, and they also now 'wait' after their interval has passed, +instantly buying once they are able to.
  • +
  • Automatic DimBoosts, Galaxies, and Big Crunches now have an input box.
  • +
  • Unique achievement rewards for multiple achievements.
  • +
  • Zero galaxies now gives 11% tickspeed.
  • +
  • Galaxies past two give diminishing returns, Faster than a Potato made easier to compensate.
  • +
  • Game now updates 20 times a second with increased performance, max autobuyer speed is not impacted.
  • +
  • Autobuyer settings are now saved in between sessions.
  • +
  • Monitor scaling issues mainly fixed.
  • +
  • Priority should be working properly.
  • +
  • Big crunch button is now less obtrusive.
  • +
  • Your screen no longer defaults to the dimensions tab when you reach infinity +(if you have broken infinity or if your fastest time to reach infinity is less than one minute).
  • +
  • More statistics have been added such as record challenge times and last ten infinities.
  • +
  • Times below one minute are now kept at two decimal points of precision.
  • +
  • Percentage increase per second for dimensions 1-8 are now kept to two decimal points of precision.
  • +
  • The reset button works better now.
  • +
` + }, + { + date: [2017, 9, 7], + name: "Challenges", + info: ` +
    +
  • Added 12 challenges.
  • +
  • Added 8 new achievements.
  • +
  • Added autobuyers.
  • +
` + }, + { + // These were originally spread throughout 28/8 to 30/8. + // But they would otherwise hold too little content on their own + date: [2017, 8, 30], + info: ` +
    +
  • Added news on top of the page.
  • +
  • Added a multiplier for completing a row of achievements.
  • +
  • New letter notation option.
  • +
  • Nerfed galaxies from +3% to +2%.
  • +
  • Added 8 new achievements.
  • +
  • Added Dimensional Sacrifice, appears at 5th dimension shift/boost.
  • +
  • More notations!
  • +
  • Bar until infinity at the bottom.
  • +
  • Some UI changes.
  • +
` + }, + { + date: [2017, 8, 24], + name: "Infinity", + info: ` +
    +
  • Now when you get to 1.7e308 antimatter, you reach infinity, and you can reset again at infinity, +gaining infinity points.
  • +
  • You can use infinity points for upgrades.
  • +
  • The game also now runs 6 hours while it is closed.
  • +
  • In addition there are some graphic updates.
  • +
` + }, + // These were originally spread throughout 3/5 to 7/5. + // But they would otherwise hold too little content on their own + { + date: [2017, 5, 7], + info: ` +
    +
  • Added this changelog, fixed money displaying problem. Added a title to the HTML.
  • +
  • The game now works offtab.
  • +
  • Fixed the bug with costs showing for example 1000 SxTg.
  • +
  • Visual update! And statistics.
  • +
  • Added export and import options.
  • +
  • Added save button although game saves every 10 seconds.
  • +
  • Slightly smaller text and added a max all button.
  • +
  • Saves should now FINALLY work properly.
  • +
` + } +]; + + +for (let i = 0; i < GameDatabase.changelog.length; i++) { + const entry = GameDatabase.changelog[i]; + entry.id = i; +} \ No newline at end of file diff --git a/javascripts/core/secret-formula/confirmation-types.js b/javascripts/core/secret-formula/confirmation-types.js index f7e9f7490..11549578b 100644 --- a/javascripts/core/secret-formula/confirmation-types.js +++ b/javascripts/core/secret-formula/confirmation-types.js @@ -1,4 +1,4 @@ -import { GameDatabase } from "./game-database.js"; +import { GameDatabase } from "./game-database"; GameDatabase.confirmationTypes = [ { @@ -56,22 +56,30 @@ GameDatabase.confirmationTypes = [ }, { name: "Glyph Undo", option: "glyphUndo", - isUnlocked: () => Teresa.has(TERESA_UNLOCKS.UNDO), + isUnlocked: () => TeresaUnlocks.undo.canBeApplied, + }, { + name: "Switch Automator Editor", + option: "switchAutomatorMode", + isUnlocked: () => Player.automatorUnlocked, }, { name: "Reset Celestial", option: "resetCelestial", - isUnlocked: () => Teresa.has(TERESA_UNLOCKS.RUN), + isUnlocked: () => TeresaUnlocks.run.canBeApplied, }, { - name: "Delete Glyph Set Save", + name: "Delete Glyph Preset", option: "deleteGlyphSetSave", isUnlocked: () => EffarigUnlock.setSaves.isUnlocked, }, { name: "Glyph Refine", option: "glyphRefine", - isUnlocked: () => Ra.has(RA_UNLOCKS.GLYPH_ALCHEMY), + isUnlocked: () => Ra.unlocks.unlockGlyphAlchemy.canBeApplied, }, { name: "Armageddon", option: "armageddon", isUnlocked: () => Pelle.isDoomed, - }, + }, { + name: "Respec Shop Purchases", + option: "respecIAP", + isUnlocked: () => true + } ]; diff --git a/javascripts/core/secret-formula/credits.js b/javascripts/core/secret-formula/credits.js new file mode 100644 index 000000000..6aefacc0f --- /dev/null +++ b/javascripts/core/secret-formula/credits.js @@ -0,0 +1,371 @@ +import { GameDatabase } from "./game-database"; + +GameDatabase.credits = { + // Must be placed in the order it is desired they appear in the credits + roles: { + 1: "Creator", + 2: "Technical Architect", + 3: "Lead Developer", + 4: "Android Developer", + 5: "Library Developer", + 6: "Developer", + 7: "Lead Design Consultant", + 8: "Design Consultant", + 9: "Modal Maker, Lady Taker, Pie Baker", + 10: "Lurker Tester", + 11: "Web Tester", + 12: "Android Tester" + }, + + // Each person must have a name and at least one role (the index of the desired role in roles). They can also have a + // second name, which will appear in parentheses besides their first. + people: [ + { + name: "Hevipelle", + name2: "Ivar Kerajärvi", + roles: 1 + }, { + name: "Razenpok", + name2: "Andrei Andreev", + roles: 2 + }, { + name: "garnet420", + roles: 3 + }, { + name: "Omsi", + roles: 3 + }, { + name: "SpectralFlame", + name2: "Christopher Yip", + roles: 3 + }, { + name: "WaitingIdly", + roles: [3, 6, 8, 11, 12] + }, { + name: "kajfik", + name2: "Jakub Kajfosz", + roles: 4 + }, { + name: "Patashu", + roles: [5, 6, 11] + }, { + name: "Dan", + roles: [6, 11] + }, { + name: "earth", + name2: "Jace Royer", + roles: [6, 9, 11, 12] + }, { + name: "Hira", + roles: [6, 11, 12] + }, { + name: "IkerStream", + name2: "Iker de Aguirre", + roles: [6, 11] + }, { + name: "L4R5", + name2: "Lars Wolf", + roles: [6, 11, 12] + }, { + name: "Pichusuperlover", + roles: [6, 8, 11] + }, { + name: "realrapidjazz", + roles: [6, 7] + }, { + name: "Scarlet", + roles: [6, 11, 12] + }, { + name: "slabdrill", + roles: 6 + }, { + name: "Acamaeda", + roles: [8, 11] + }, { + name: "Dravitar", + name2: "Alex Henderson", + roles: 10 + }, { + name: "Aesis", + roles: 11 + }, { + name: "AFYINEE", + name2: "Gabriel HADDAG", + roles: 11 + }, { + name: "Alexitato", + roles: 11 + }, { + name: "Anno", + roles: 11 + }, { + name: "Archa", + name2: "Myresa", + roles: [11, 12] + }, { + name: "ArrowBounce", + name2: "Timothy Su", + roles: 11 + }, { + name: "Birb", + name2: "Kelsey Black", + roles: 11 + }, { + name: "Boo", + name2: "Jean-Christophe Bourgault", + roles: 11 + }, { + name: "CaptainGalaxy", + name2: "Ovidijus Točelis", + roles: 11 + }, { + name: "ChaoticHans", + roles: [11, 12] + }, { + name: "cubic frog", + roles: 11 + }, { + name: "dankesehr", + roles: 11 + }, { + name: "Davixx", + name2: "Davide Fedele", + roles: 11 + }, { + name: "Empireus", + roles: 11 + }, { + name: "GirixK", + name2: "Nikola Jelinčić", + roles: [11, 12] + }, { + name: "GoldenTritium", + roles: [11, 12] + }, { + name: "Kael", + roles: 11 + }, { + name: "Lynn", + roles: 11 + }, { + name: "Merp", + roles: 11 + }, { + name: "philipebreaker", + name2: "Philipe", + roles: 11 + }, { + name: "Phillip Marshall", + roles: 11 + }, { + name: "Phoenix", + roles: 11 + }, { + name: "Reda Kotob", + roles: 11 + }, { + name: "Saturnus", + roles: 11 + }, { + name: "SereKabii", + roles: 11 + }, { + name: "Sheer", + roles: 11 + }, { + name: "sirusi", + name2: "Vinícius Oliveira Martins", + roles: 11 + }, { + name: "Spanosa", + name2: "Jared K", + roles: 11 + }, { + name: "Sparticle999", + roles: 11 + }, { + name: "SpicyCrusader13", + roles: [11, 12] + }, { + name: "Storm", + roles: 11 + }, { + name: "SzyszakS", + roles: 11 + }, { + name: "Tacitus", + roles: 11 + }, { + name: "Typh", + roles: 11 + }, { + name: "Vnge", + name2: "Ben Parrish", + roles: [11, 12] + }, { + name: "Xemadus", + name2: "Jonathan Gibson", + roles: 11 + }, { + name: "Young Woo Joo", + roles: 11 + }, { + name: "Zipi", + roles: 11 + }, { + name: "about:blank", + roles: 12 + }, { + name: "ÆiOuF", + roles: 12 + }, { + name: "Anjinho01", + roles: 12 + }, { + name: "Anthios", + roles: 12 + }, { + name: "Auti", + name2: "Alice Tolle", + roles: 12 + }, { + name: "Buck", + roles: 12 + }, { + name: "Barrin84", + roles: 12 + }, { + name: "ChizuX", + roles: 12 + }, { + name: "Circle", + roles: 12 + }, { + name: "Crinkly Weasel", + name2: "Aaryan Sarawgi", + roles: 12 + }, { + name: "DarthDie", + name2: "Briar Bowser", + roles: 12 + }, { + name: "Epsilon", + name2: "Coolguystorm", + roles: 12 + }, { + name: "Firecracker", + roles: 12 + }, { + name: "Gaunter", + roles: 12 + }, { + name: "HarrisL2", + roles: 12 + }, { + name: "Hellbach", + name2: "Asher Günther", + roles: 12 + }, { + name: "hen-ben", + name2: "Henry Ellenberg", + roles: 12 + }, { + name: "ImpossibleSalsa", + roles: 12 + }, { + name: "Johanniklas", + name2: "Jan-Niklas Petersen", + roles: 12 + }, { + name: "kaislash", + name2: "Lily", + roles: 12 + }, { + name: "Kirku", + name2: "Fabian Makowski", + roles: 12 + }, { + name: "Kirin", + name2: "Arthur", + roles: 12 + }, { + name: "Mirai", + roles: 12 + }, { + name: "Monoma", + name2: "ARoman Ruiz", + roles: 12 + }, { + name: "Nani", + roles: 12 + }, { + name: "netweak", + roles: 12 + }, { + name: "NotBrewst", + name2: "Luc Leblanc", + roles: 12 + }, { + name: "opdollar", + name2: "Zane Coole", + roles: 12 + }, { + name: "Pavlxiiv", + roles: 12 + }, { + name: "Porygon-Z", + roles: 12 + }, { + name: "PotatoTIAB", + roles: 12 + }, { + name: "Razor", + roles: 12 + }, { + name: "Razvan Cercel", + roles: 12 + }, { + name: "ReacTivity", + roles: 12 + }, { + name: "Rukimix", + roles: 12 + }, { + name: "Skunky", + name2: "Lukas", + roles: 12 + }, { + name: "Socks", + name2: "Hannah Pocks", + roles: 12 + }, { + name: "Sweets the Alien", + roles: 12 + }, { + name: "Taylor Reeves", + roles: 12 + }, { + name: "Tim Wong", + roles: 12 + }, { + name: "tragedt", + name2: "Ethan Manninen", + roles: 12 + }, { + name: "Valentine Clarissa Alanis Star Z", + roles: 12 + }, { + name: "vanadium_void", + roles: 12 + }, { + name: "X3N0_32", + roles: 12 + }, { + name: "ZylaKat", + name2: "Katherine Goforth-Harbin", + roles: 12 + } + ] +}; + +GameDatabase.credits.roles.count = Object.keys(GameDatabase.credits.roles).length; diff --git a/javascripts/core/secret-formula/eternity/dilation-upgrades.js b/javascripts/core/secret-formula/eternity/dilation-upgrades.js index b81d97106..a8f757056 100644 --- a/javascripts/core/secret-formula/eternity/dilation-upgrades.js +++ b/javascripts/core/secret-formula/eternity/dilation-upgrades.js @@ -1,190 +1,197 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; -GameDatabase.eternity.dilation = (function() { - function rebuyableCost(initialCost, increment, id) { - return Decimal.multiply(initialCost, Decimal.pow(increment, player.dilation.rebuyables[id])); - } - function rebuyable(config) { - return { - id: config.id, - cost: () => rebuyableCost(config.initialCost, config.increment, config.id), - initialCost: config.initialCost, - increment: config.increment, - description: config.description, - effect: () => config.effect(player.dilation.rebuyables[config.id]), - formatEffect: config.formatEffect, - formatCost: config.formatCost, - purchaseCap: config.purchaseCap, - reachedCap: () => player.dilation.rebuyables[config.id] >= config.purchaseCap, - pelleOnly: Boolean(config.pelleOnly), - rebuyable: true - }; - } +function rebuyableCost(initialCost, increment, id) { + return Decimal.multiply(initialCost, Decimal.pow(increment, player.dilation.rebuyables[id])); +} +function rebuyable(config) { return { - dtGain: rebuyable({ - id: 1, - initialCost: 1e5, - increment: 10, - description: () => - (SingularityMilestone.dilatedTimeFromSingularities.isUnlocked - ? `${formatX(2 * SingularityMilestone.dilatedTimeFromSingularities.effectValue, 2, 2)} Dilated Time gain` - : "Double Dilated Time gain"), - effect: bought => { - const base = SingularityMilestone.dilatedTimeFromSingularities.isUnlocked - ? 2 * SingularityMilestone.dilatedTimeFromSingularities.effectValue - : 2; - return Decimal.pow(base, bought); - }, - formatEffect: value => formatX(value, 2), - formatCost: value => format(value, 2), - purchaseCap: Number.MAX_VALUE - }), - galaxyThreshold: rebuyable({ - id: 2, - initialCost: 1e6, - increment: 100, - description: () => - (Perk.bypassTGReset.isBought - ? "Reset Tachyon Galaxies, but lower their threshold" - : "Reset Dilated Time and Tachyon Galaxies, but lower their threshold"), - // The 38th purchase is at 1e80, and is the last purchase. - effect: bought => (bought < 38 ? Math.pow(0.8, bought) : 0), - formatEffect: effect => { - if (effect === 0) return `${formatX(getTachyonGalaxyMult(effect), 4, 4)}`; - const nextEffect = effect === Math.pow(0.8, 37) ? 0 : 0.8 * effect; - return `${formatX(getTachyonGalaxyMult(effect), 4, 4)} ➜ - Next: ${formatX(getTachyonGalaxyMult(nextEffect), 4, 4)}`; - }, - formatCost: value => format(value, 2), - purchaseCap: 38 - }), - tachyonGain: rebuyable({ - id: 3, - initialCost: 1e7, - increment: 20, - description: () => { - if (Pelle.isDoomed) return `Multiply the amount of Tachyon Particles gained by ${formatInt(1)}`; - if (Enslaved.isRunning) return `Multiply the amount of Tachyon Particles gained - by ${Math.pow(3, Enslaved.tachyonNerf).toFixed(2)}`; - return "Triple the amount of Tachyon Particles gained"; - }, - effect: bought => { - if (Pelle.isDoomed) return DC.D1.pow(bought); - return DC.D3.pow(bought); - }, - formatEffect: value => formatX(value, 2), - formatCost: value => format(value, 2), - purchaseCap: Number.MAX_VALUE - }), - doubleGalaxies: { - id: 4, - cost: 5e6, - description: () => `Gain twice as many Tachyon Galaxies, up to ${formatInt(1000)}`, - effect: 2 - }, - tdMultReplicanti: { - id: 5, - cost: 1e9, - description: () => { - const rep10 = replicantiMult().pLog10(); - let multiplier = "0.1"; - if (rep10 > 9000) { - const ratio = DilationUpgrade.tdMultReplicanti.effectValue.pLog10() / rep10; - if (ratio < 0.095) { - multiplier = ratio.toFixed(2); - } - } - return `Time Dimensions are affected by Replicanti multiplier ${formatPow(multiplier, 1, 3)}, reduced - effect above ${formatX(DC.E9000)}`; - }, - effect: () => { - let rep10 = replicantiMult().pLog10() * 0.1; - rep10 = rep10 > 9000 ? 9000 + 0.5 * (rep10 - 9000) : rep10; - return Decimal.pow10(rep10); - }, - formatEffect: value => formatX(value, 2, 1) - }, - ndMultDT: { - id: 6, - cost: 5e7, - description: "Antimatter Dimension multiplier based on Dilated Time, unaffected by Time Dilation", - effect: () => Currency.dilatedTime.value.pow(308).clampMin(1), - formatEffect: value => formatX(value, 2, 1) - }, - ipMultDT: { - id: 7, - cost: 2e12, - description: "Gain a multiplier to Infinity Points based on Dilated Time", - effect: () => Currency.dilatedTime.value.pow(1000).clampMin(1), - formatEffect: value => formatX(value, 2, 1), - cap: () => Effarig.eternityCap - }, - timeStudySplit: { - id: 8, - cost: 1e10, - description: "You can buy all three Time Study paths from the Dimension Split" - }, - dilationPenalty: { - id: 9, - cost: 1e11, - description: () => `Reduce the Dilation penalty (${formatPow(1.05, 2, 2)} after reduction)`, - effect: 1.05, - }, - ttGenerator: { - id: 10, - cost: 1e15, - description: "Generate Time Theorems based on Tachyon Particles", - effect: () => Currency.tachyonParticles.value.div(20000), - formatEffect: value => `${format(value, 2, 1)}/sec` - }, - dtGainPelle: rebuyable({ - id: 11, - initialCost: 1e14, - increment: 100, - pelleOnly: true, - description: () => `${formatX(5)} Dilated Time gain`, - effect: bought => Decimal.pow(5, bought), - formatEffect: value => formatX(value, 2), - formatCost: value => format(value, 2), - purchaseCap: Number.MAX_VALUE - }), - galaxyMultiplier: rebuyable({ - id: 12, - initialCost: 1e15, - increment: 1000, - pelleOnly: true, - description: () => "Multiply Tachyon Galaxies gained", - effect: bought => bought + 1, - formatEffect: value => `${formatX(value, 2)} ➜ ${formatX(value + 1, 2)}`, - formatCost: value => format(value, 2), - purchaseCap: Number.MAX_VALUE - }), - tickspeedPower: rebuyable({ - id: 13, - initialCost: 1e16, - increment: 1e4, - pelleOnly: true, - description: () => `Gain a power to tickspeed effect`, - effect: bought => 1 + bought * 0.03, - formatEffect: value => `${formatPow(value, 2, 2)} ➜ ${formatPow(value + 0.03, 2, 2)}`, - formatCost: value => format(value, 2), - purchaseCap: Number.MAX_VALUE - }), - galaxyThresholdPelle: { - id: 14, - cost: 1e45, - pelleOnly: true, - description: "Cubic root Tachyon Galaxy threshold", - effect: 1 / 3 - }, - flatDilationMult: { - id: 15, - cost: 1e55, - pelleOnly: true, - description: () => `Gain more Dilated Time based on EP`, - effect: () => 1e9 ** Math.min((Math.max(player.eternityPoints.log10() - 1500, 0) / 2500) ** 1.2, 1), - formatEffect: value => formatX(value, 2, 2) - }, + id: config.id, + cost: () => rebuyableCost(config.initialCost, config.increment, config.id), + initialCost: config.initialCost, + increment: config.increment, + description: config.description, + effect: () => config.effect(player.dilation.rebuyables[config.id]), + formatEffect: config.formatEffect, + formatCost: config.formatCost, + purchaseCap: config.purchaseCap, + reachedCap: () => player.dilation.rebuyables[config.id] >= config.purchaseCap, + pelleOnly: Boolean(config.pelleOnly), + rebuyable: true }; -}()); +} + +GameDatabase.eternity.dilation = { + dtGain: rebuyable({ + id: 1, + initialCost: 1e5, + increment: 10, + description: () => + ((SingularityMilestone.dilatedTimeFromSingularities.canBeApplied || Achievement(187).canBeApplied) + ? `${formatX(2 * Effects.product( + SingularityMilestone.dilatedTimeFromSingularities, + Achievement(187) + ), 2, 2)} Dilated Time gain` + : "Double Dilated Time gain"), + effect: bought => { + const base = 2 * Effects.product( + SingularityMilestone.dilatedTimeFromSingularities, + Achievement(187) + ); + return Decimal.pow(base, bought); + }, + formatEffect: value => { + const nonInteger = SingularityMilestone.dilatedTimeFromSingularities.canBeApplied || + Achievement(187).canBeApplied; + return formatX(value, 2, nonInteger ? 2 : 0); + }, + formatCost: value => format(value, 2), + purchaseCap: Number.MAX_VALUE + }), + galaxyThreshold: rebuyable({ + id: 2, + initialCost: 1e6, + increment: 100, + description: () => + (Perk.bypassTGReset.isBought + ? "Reset Tachyon Galaxies, but lower their threshold" + : "Reset Dilated Time and Tachyon Galaxies, but lower their threshold"), + // The 38th purchase is at 1e80, and is the last purchase. + effect: bought => (bought < 38 ? Math.pow(0.8, bought) : 0), + formatEffect: effect => { + if (effect === 0) return `${formatX(getTachyonGalaxyMult(effect), 4, 4)}`; + const nextEffect = effect === Math.pow(0.8, 37) ? 0 : 0.8 * effect; + return `${formatX(getTachyonGalaxyMult(effect), 4, 4)} ➜ + Next: ${formatX(getTachyonGalaxyMult(nextEffect), 4, 4)}`; + }, + formatCost: value => format(value, 2), + purchaseCap: 38 + }), + tachyonGain: rebuyable({ + id: 3, + initialCost: 1e7, + increment: 20, + description: () => { + if (Pelle.isDoomed) return `Multiply the amount of Tachyon Particles gained by ${formatInt(1)}`; + if (Enslaved.isRunning) return `Multiply the amount of Tachyon Particles gained + by ${Math.pow(3, Enslaved.tachyonNerf).toFixed(2)}`; + return "Triple the amount of Tachyon Particles gained"; + }, + effect: bought => { + if (Pelle.isDoomed) return DC.D1.pow(bought); + return DC.D3.pow(bought); + }, + formatEffect: value => formatX(value, 2), + formatCost: value => format(value, 2), + purchaseCap: Number.MAX_VALUE + }), + doubleGalaxies: { + id: 4, + cost: 5e6, + description: () => `Gain twice as many Tachyon Galaxies, up to ${formatInt(1000)}`, + effect: 2 + }, + tdMultReplicanti: { + id: 5, + cost: 1e9, + description: () => { + const rep10 = replicantiMult().pLog10(); + let multiplier = "0.1"; + if (rep10 > 9000) { + const ratio = DilationUpgrade.tdMultReplicanti.effectValue.pLog10() / rep10; + if (ratio < 0.095) { + multiplier = ratio.toFixed(2); + } + } + return `Time Dimensions are affected by Replicanti multiplier ${formatPow(multiplier, 1, 3)}, reduced + effect above ${formatX(DC.E9000)}`; + }, + effect: () => { + let rep10 = replicantiMult().pLog10() * 0.1; + rep10 = rep10 > 9000 ? 9000 + 0.5 * (rep10 - 9000) : rep10; + return Decimal.pow10(rep10); + }, + formatEffect: value => formatX(value, 2, 1) + }, + ndMultDT: { + id: 6, + cost: 5e7, + description: "Antimatter Dimension multiplier based on Dilated Time, unaffected by Time Dilation", + effect: () => Currency.dilatedTime.value.pow(308).clampMin(1), + formatEffect: value => formatX(value, 2, 1) + }, + ipMultDT: { + id: 7, + cost: 2e12, + description: "Gain a multiplier to Infinity Points based on Dilated Time", + effect: () => Currency.dilatedTime.value.pow(1000).clampMin(1), + formatEffect: value => formatX(value, 2, 1), + cap: () => Effarig.eternityCap + }, + timeStudySplit: { + id: 8, + cost: 1e10, + description: "You can buy all three Time Study paths from the Dimension Split" + }, + dilationPenalty: { + id: 9, + cost: 1e11, + description: () => `Reduce the Dilation penalty (${formatPow(1.05, 2, 2)} after reduction)`, + effect: 1.05, + }, + ttGenerator: { + id: 10, + cost: 1e15, + description: "Generate Time Theorems based on Tachyon Particles", + effect: () => Currency.tachyonParticles.value.div(20000), + formatEffect: value => `${format(value, 2, 1)}/sec` + }, + dtGainPelle: rebuyable({ + id: 11, + initialCost: 1e14, + increment: 100, + pelleOnly: true, + description: () => `${formatX(5)} Dilated Time gain`, + effect: bought => Decimal.pow(5, bought), + formatEffect: value => formatX(value, 2), + formatCost: value => format(value, 2), + purchaseCap: Number.MAX_VALUE + }), + galaxyMultiplier: rebuyable({ + id: 12, + initialCost: 1e15, + increment: 1000, + pelleOnly: true, + description: "Multiply Tachyon Galaxies gained, applies after TG doubling upgrade", + effect: bought => bought + 1, + formatEffect: value => `${formatX(value, 2)} ➜ ${formatX(value + 1, 2)}`, + formatCost: value => format(value, 2), + purchaseCap: Number.MAX_VALUE + }), + tickspeedPower: rebuyable({ + id: 13, + initialCost: 1e16, + increment: 1e4, + pelleOnly: true, + description: "Gain a power to Tickspeed", + effect: bought => 1 + bought * 0.03, + formatEffect: value => `${formatPow(value, 2, 2)} ➜ ${formatPow(value + 0.03, 2, 2)}`, + formatCost: value => format(value, 2), + purchaseCap: Number.MAX_VALUE + }), + galaxyThresholdPelle: { + id: 14, + cost: 1e45, + pelleOnly: true, + description: "Apply a cube root to the Tachyon Galaxy threshold", + effect: 1 / 3 + }, + flatDilationMult: { + id: 15, + cost: 1e55, + pelleOnly: true, + description: () => `Gain more Dilated Time based on current EP`, + effect: () => 1e9 ** Math.min((Math.max(player.eternityPoints.log10() - 1500, 0) / 2500) ** 1.2, 1), + formatEffect: value => formatX(value, 2, 2) + }, +}; diff --git a/javascripts/core/secret-formula/eternity/eternity-milestones.js b/javascripts/core/secret-formula/eternity/eternity-milestones.js index 3eec87037..e62b1bc65 100644 --- a/javascripts/core/secret-formula/eternity/eternity-milestones.js +++ b/javascripts/core/secret-formula/eternity/eternity-milestones.js @@ -1,4 +1,4 @@ -import { GameDatabase } from "../game-database.js"; +import { GameDatabase } from "../game-database"; GameDatabase.eternity.milestones = { autobuyerIPMult: { @@ -17,7 +17,7 @@ GameDatabase.eternity.milestones = { keepInfinityUpgrades: { eternities: 4, reward: "You start Eternity with all Infinity Upgrades", - pelleObsolete: () => PelleUpgrade.keepInfinityUpgrades.isBought + givenByPelle: () => PelleUpgrade.keepInfinityUpgrades.isBought }, bigCrunchModes: { eternities: 5, @@ -41,12 +41,12 @@ GameDatabase.eternity.milestones = { eternities: 7, reward: `You complete Infinity Challenges as soon as you unlock them, and keep the Dimensional Sacrifice Autobuyer`, - pelleObsolete: () => PelleUpgrade.keepInfinityChallenges.isBought, + pelleUseless: true }, keepBreakUpgrades: { eternities: 8, reward: "You start Eternity with all Break Infinity Upgrades", - pelleObsolete: () => PelleUpgrade.keepBreakInfinityUpgrades.isBought, + givenByPelle: () => PelleUpgrade.keepBreakInfinityUpgrades.isBought, }, autobuyMaxGalaxies: { eternities: 9, @@ -55,47 +55,47 @@ GameDatabase.eternity.milestones = { unlockReplicanti: { eternities: 10, reward: "You start with Replicanti unlocked", - pelleObsolete: () => PelleUpgrade.replicantiStayUnlocked.isBought, + givenByPelle: () => PelleUpgrade.replicantiStayUnlocked.isBought, }, autobuyerID1: { eternities: 11, reward: "Unlock the 1st Infinity Dimension Autobuyer", - pelleObsolete: () => PelleUpgrade.IDAutobuyers.isBought, + givenByPelle: () => PelleUpgrade.IDAutobuyers.isBought, }, autobuyerID2: { eternities: 12, reward: "Unlock the 2nd Infinity Dimension Autobuyer", - pelleObsolete: () => PelleUpgrade.IDAutobuyers.isBought, + givenByPelle: () => PelleUpgrade.IDAutobuyers.isBought, }, autobuyerID3: { eternities: 13, reward: "Unlock the 3rd Infinity Dimension Autobuyer", - pelleObsolete: () => PelleUpgrade.IDAutobuyers.isBought, + givenByPelle: () => PelleUpgrade.IDAutobuyers.isBought, }, autobuyerID4: { eternities: 14, reward: "Unlock the 4th Infinity Dimension Autobuyer", - pelleObsolete: () => PelleUpgrade.IDAutobuyers.isBought, + givenByPelle: () => PelleUpgrade.IDAutobuyers.isBought, }, autobuyerID5: { eternities: 15, reward: "Unlock the 5th Infinity Dimension Autobuyer", - pelleObsolete: () => PelleUpgrade.IDAutobuyers.isBought, + givenByPelle: () => PelleUpgrade.IDAutobuyers.isBought, }, autobuyerID6: { eternities: 16, reward: "Unlock the 6th Infinity Dimension Autobuyer", - pelleObsolete: () => PelleUpgrade.IDAutobuyers.isBought, + givenByPelle: () => PelleUpgrade.IDAutobuyers.isBought, }, autobuyerID7: { eternities: 17, reward: "Unlock the 7th Infinity Dimension Autobuyer", - pelleObsolete: () => PelleUpgrade.IDAutobuyers.isBought, + givenByPelle: () => PelleUpgrade.IDAutobuyers.isBought, }, autobuyerID8: { eternities: 18, reward: "Unlock the 8th Infinity Dimension Autobuyer", - pelleObsolete: () => PelleUpgrade.IDAutobuyers.isBought, + givenByPelle: () => PelleUpgrade.IDAutobuyers.isBought, }, autoUnlockID: { eternities: 25, @@ -113,17 +113,17 @@ GameDatabase.eternity.milestones = { autobuyerReplicantiChance: { eternities: 50, reward: "Unlock the Replicanti Chance Upgrade Autobuyer", - pelleObsolete: () => PelleUpgrade.replicantiAutobuyers.isBought, + givenByPelle: () => PelleUpgrade.replicantiAutobuyers.isBought, }, autobuyerReplicantiInterval: { eternities: 60, reward: "Unlock the Replicanti Interval Upgrade Autobuyer", - pelleObsolete: () => PelleUpgrade.replicantiAutobuyers.isBought, + givenByPelle: () => PelleUpgrade.replicantiAutobuyers.isBought, }, autobuyerReplicantiMaxGalaxies: { eternities: 80, reward: "Unlock the Max Replicanti Galaxy Upgrade Autobuyer", - pelleObsolete: () => PelleUpgrade.replicantiAutobuyers.isBought, + givenByPelle: () => PelleUpgrade.replicantiAutobuyers.isBought, }, autobuyerEternity: { eternities: 100, @@ -159,7 +159,7 @@ GameDatabase.eternity.milestones = { }, activeCondition: () => (player.options.offlineProgress ? `Must be outside of Normal/Infinity Challenges and outside of EC4 and EC12, - the Infinity Autobuyer must be turned on and set to time mode with less than ${formatInt(60)} seconds, + the Big Crunch Autobuyer must be turned on and set to time mode with less than ${formatInt(60)} seconds, and the Eternity Autobuyer must be turned off.` : ""), } diff --git a/javascripts/core/secret-formula/eternity/eternity-upgrades.js b/javascripts/core/secret-formula/eternity/eternity-upgrades.js index 250d6bdd7..f5d17939d 100644 --- a/javascripts/core/secret-formula/eternity/eternity-upgrades.js +++ b/javascripts/core/secret-formula/eternity/eternity-upgrades.js @@ -1,5 +1,5 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; GameDatabase.eternity.upgrades = { idMultEP: { diff --git a/javascripts/core/secret-formula/eternity/time-studies/dilation-time-studies.js b/javascripts/core/secret-formula/eternity/time-studies/dilation-time-studies.js index b7f00a5d8..0ab078a47 100644 --- a/javascripts/core/secret-formula/eternity/time-studies/dilation-time-studies.js +++ b/javascripts/core/secret-formula/eternity/time-studies/dilation-time-studies.js @@ -1,4 +1,4 @@ -import { GameDatabase } from "../../game-database.js"; +import { GameDatabase } from "../../game-database"; GameDatabase.eternity.timeStudies.dilation = [ { @@ -7,14 +7,14 @@ GameDatabase.eternity.timeStudies.dilation = [ cost: 5000, requirement: () => { const ttRequirement = Currency.timeTheorems.max.gte(TimeStudy.dilation.totalTimeTheoremRequirement); - if (Ra.has(RA_UNLOCKS.AUTO_DILATION_UNLOCK) && + if (Ra.unlocks.autoUnlockDilation.canBeApplied && ttRequirement && !isInCelestialReality() && !Pelle.isDoomed ) { return true; } const tsRequirement = [231, 232, 233, 234].some(id => TimeStudy(id).isBought); - if (Perk.bypassECDilation.isBought && !Pelle.isDoomed) return tsRequirement; + if (Perk.bypassECDilation.canBeApplied) return tsRequirement; const ecRequirement = EternityChallenge(11).isFullyCompleted && EternityChallenge(12).isFullyCompleted; return tsRequirement && ecRequirement && ttRequirement; } @@ -51,6 +51,7 @@ GameDatabase.eternity.timeStudies.dilation = [ cost: () => 1, requirement: () => TimeStudy.timeDimension(8).isBought && player.records.thisReality.maxEP.exponent >= 4000 && - (Perk.firstPerk.isBought ? true : Achievements.preReality.every(a => a.isUnlocked)) + (Perk.firstPerk.isBought || Achievements.preReality.every(a => a.isUnlocked)) && + !Pelle.isDoomed } ]; diff --git a/javascripts/core/secret-formula/eternity/time-studies/ec-time-studies.js b/javascripts/core/secret-formula/eternity/time-studies/ec-time-studies.js index d0913bf5f..adf5bb08a 100644 --- a/javascripts/core/secret-formula/eternity/time-studies/ec-time-studies.js +++ b/javascripts/core/secret-formula/eternity/time-studies/ec-time-studies.js @@ -1,5 +1,5 @@ -import { GameDatabase } from "../../game-database.js"; -import { DC } from "../../../constants.js"; +import { DC } from "../../../constants"; +import { GameDatabase } from "../../game-database"; GameDatabase.eternity.timeStudies.ec = [ { diff --git a/javascripts/core/secret-formula/eternity/time-studies/normal-time-studies.js b/javascripts/core/secret-formula/eternity/time-studies/normal-time-studies.js index fac941610..a15b0b2ee 100644 --- a/javascripts/core/secret-formula/eternity/time-studies/normal-time-studies.js +++ b/javascripts/core/secret-formula/eternity/time-studies/normal-time-studies.js @@ -1,5 +1,20 @@ -import { GameDatabase } from "../../game-database.js"; -import { DC } from "../../../constants.js"; +import { DC } from "../../../constants"; +import { GameDatabase } from "../../game-database"; + +const thisInfinityMult = thisInfinity => { + // All "this inf time" or "best inf time" mults are * 10 + const scaledInfinity = thisInfinity * 10 + 1; + const cappedInfinity = Math.min(Math.pow(scaledInfinity, 0.125), 500); + return DC.D15.pow(Math.log(scaledInfinity) * cappedInfinity); +}; +const passiveIPMult = () => { + const isEffarigLimited = Effarig.isRunning && Effarig.currentStage === EFFARIG_STAGES.ETERNITY; + const normalValue = Perk.studyPassive.isBought ? 1e50 : 1e25; + return isEffarigLimited + ? Math.min(normalValue, Effarig.eternityCap.toNumber()) + : normalValue; +}; + /** * List of time study specifications and attributes @@ -17,668 +32,654 @@ import { DC } from "../../../constants.js"; * @property {String} formatEffect Formatting function for effects, if the default formatting isn't appropriate * } */ -GameDatabase.eternity.timeStudies.normal = (function() { - const thisInfinityMult = thisInfinity => { - // All "this inf time" or "best inf time" mults are * 10 - const scaledInfinity = thisInfinity * 10 + 1; - const cappedInfinity = Math.min(Math.pow(scaledInfinity, 0.125), 500); - return DC.D15.pow(Math.log(scaledInfinity) * cappedInfinity); - }; - const passiveIPMult = () => { - const isEffarigLimited = Effarig.isRunning && Effarig.currentStage === EFFARIG_STAGES.ETERNITY; - const normalValue = Perk.studyPassive.isBought ? 1e50 : 1e25; - return isEffarigLimited - ? Math.min(normalValue, Effarig.eternityCap.toNumber()) - : normalValue; - }; - return [ - { - id: 11, - cost: 1, - // All requirements of an empty array will always evaluate to true, so this study is always purchasable - requirement: [], - reqType: TS_REQUIREMENT_TYPE.ALL, - description: "Tickspeed affects 1st Time Dimension with reduced effect", - effect: () => { - const tickspeed = Tickspeed.current.dividedBy(1000); - const firstPart = tickspeed.pow(0.005).times(0.95); - const secondPart = tickspeed.pow(0.0003).times(0.05); - return firstPart.plus(secondPart).reciprocate(); - }, - cap: DC.E2500, - formatEffect: value => formatX(value, 2, 1) +GameDatabase.eternity.timeStudies.normal = [ + { + id: 11, + cost: 1, + // All requirements of an empty array will always evaluate to true, so this study is always purchasable + requirement: [], + reqType: TS_REQUIREMENT_TYPE.ALL, + description: "Tickspeed affects 1st Time Dimension with reduced effect", + effect: () => { + const tickspeed = Tickspeed.current.dividedBy(1000); + const firstPart = tickspeed.pow(0.005).times(0.95); + const secondPart = tickspeed.pow(0.0003).times(0.05); + return firstPart.plus(secondPart).reciprocate(); }, - { - id: 21, - cost: 3, - requirement: [11], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: () => `Improve Replicanti multiplier formula to - (log2(x)${formatPow(2)})+x${formatPow(0.032, 3, 3)}`, - effect: () => Replicanti.amount.pow(0.032) + cap: DC.E2500, + formatEffect: value => formatX(value, 2, 1) + }, + { + id: 21, + cost: 3, + requirement: [11], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: () => `Improve Replicanti multiplier formula to + (log2(x)${formatPow(2)})+x${formatPow(0.032, 3, 3)}`, + effect: () => Replicanti.amount.pow(0.032) + }, + { + id: 22, + cost: 2, + requirement: [11], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: () => `Replicanti interval limit ${formatInt(50)}ms ➜ ${formatInt(1)}ms`, + effect: 1 + }, + { + id: 31, + cost: 3, + requirement: [21], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: () => `Powers up bonuses that are based on your Infinities (Bonuses${formatPow(4)})`, + effect: 4 + }, + { + id: 32, + cost: 2, + requirement: [22], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: `You gain more Infinities based on Dimension Boosts`, + effect: () => Math.max(DimBoost.totalBoosts, 1), + formatEffect: value => formatX(value) + }, + { + id: 33, + cost: 2, + requirement: [22], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: "You keep half of your Replicanti Galaxies on Infinity" + }, + { + id: 41, + cost: 4, + requirement: [31], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: () => `All Galaxies give a ${formatX(DC.D1_2, 1, 1)} multiplier to Infinity Points gained`, + effect: () => DC.D1_2.pow(Replicanti.galaxies.total + player.galaxies + player.dilation.totalTachyonGalaxies), + formatEffect: value => formatX(value, 2, 1) + }, + { + id: 42, + cost: 6, + requirement: [32], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: () => `Antimatter Galaxy requirement increases by ${formatInt(52)} + 8th Dimensions instead of ${formatInt(60)}`, + effect: 52 + }, + { + id: 51, + cost: 3, + requirement: [41, 42], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: () => `You gain ${formatX(1e15)} more Infinity Points`, + effect: 1e15 + }, + { + id: 61, + cost: 3, + requirement: [51], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: () => `You gain ${formatX(15)} more Eternity Points`, + effect: 15 + }, + { + id: 62, + cost: 3, + requirement: [42, () => Perk.bypassEC5Lock.isBought || EternityChallenge(5).completions > 0], + reqType: TS_REQUIREMENT_TYPE.ALL, + description: () => `You gain Replicanti ${formatInt(3)} times faster`, + effect: 3 + }, + { + id: 71, + cost: 4, + requirement: [61, () => Perk.studyECRequirement.isBought || !EternityChallenge(12).isUnlocked], + reqType: TS_REQUIREMENT_TYPE.DIMENSION_PATH, + description: "Dimensional Sacrifice affects all other Antimatter Dimensions with reduced effect", + effect: () => Sacrifice.totalBoost.pow(0.25).clampMin(1), + cap: DC.E210000, + formatEffect: value => formatX(value, 2, 1) + }, + { + id: 72, + cost: 6, + requirement: [61, + () => Perk.studyECRequirement.isBought || + (!EternityChallenge(11).isUnlocked && !EternityChallenge(12).isUnlocked)], + reqType: TS_REQUIREMENT_TYPE.DIMENSION_PATH, + description: "Dimensional Sacrifice affects 4th Infinity Dimension with greatly reduced effect", + effect: () => Sacrifice.totalBoost.pow(0.04).clampMin(1), + cap: DC.E30000, + formatEffect: value => formatX(value, 2, 1) + }, + { + id: 73, + cost: 5, + requirement: [61, () => Perk.studyECRequirement.isBought || !EternityChallenge(11).isUnlocked], + reqType: TS_REQUIREMENT_TYPE.DIMENSION_PATH, + description: "Dimensional Sacrifice affects 3rd Time Dimension with greatly reduced effect", + effect: () => Sacrifice.totalBoost.pow(0.005).clampMin(1), + cap: DC.E1300, + formatEffect: value => formatX(value, 2, 1) + }, + { + id: 81, + cost: 4, + requirement: [71], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: () => `Base Dimension Boost power becomes ${formatX(10)}`, + effect: 10 + }, + { + id: 82, + cost: 6, + requirement: [72], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: "Dimension Boosts affect Infinity Dimensions", + effect: () => DC.D1_0000109.pow(Math.pow(DimBoost.totalBoosts, 2)), + cap: DC.E1E7, + formatEffect: value => formatX(value, 2, 1) + }, + { + id: 83, + cost: 5, + requirement: [73], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: "Dimension Boost multiplier based on tick upgrades gained from TDs", + effect: () => DC.D1_0004.pow(player.totalTickGained), + cap: DC.E30, + formatEffect: value => formatX(value, 2, 1) + }, + { + id: 91, + cost: 4, + requirement: [81], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: "Antimatter Dimension multiplier based on time spent in this Eternity", + effect: () => Decimal.pow10(Math.min(Time.thisEternity.totalMinutes, 20) * 15), + cap: DC.E300, + formatEffect: value => formatX(value, 2, 1) + }, + { + id: 92, + cost: 5, + requirement: [82], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: "Infinity Dimension multiplier based on fastest Eternity time", + effect: () => DC.D2.pow(60 / Math.max(Time.bestEternity.totalSeconds, 2)), + cap: DC.C2P30, + formatEffect: value => formatX(value, 2, 1) + }, + { + id: 93, + cost: 7, + requirement: [83], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: "Time Dimension multiplier based on tick upgrades gained", + effect: () => Decimal.pow(player.totalTickGained, 0.25).clampMin(1), + formatEffect: value => formatX(value, 2, 1) + }, + { + id: 101, + cost: 4, + requirement: [91], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: "Antimatter Dimension multiplier equal to Replicanti amount", + effect: () => Decimal.max(Replicanti.amount, 1), + formatEffect: value => formatX(value, 2, 1) + }, + { + id: 102, + cost: 6, + requirement: [92], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: "Replicanti Galaxies boost Replicanti multiplier", + effect: () => DC.D5.pow(player.replicanti.galaxies), + formatEffect: value => formatX(value, 2, 1) + }, + { + id: 103, + cost: 6, + requirement: [93], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: "Time Dimension multiplier equal to Replicanti Galaxy amount", + effect: () => Math.max(player.replicanti.galaxies, 1), + formatEffect: value => formatX(value, 2, 0) + }, + { + id: 111, + cost: 12, + requirement: [101, 102, 103], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: () => (Achievement(103).canBeApplied + ? `Make the Infinity Point formula better log(x/${formatFloat(307.8, 1)}) ➜ log(x/${formatInt(285)})` + : `Make the Infinity Point formula better log(x/${formatInt(308)}) ➜ log(x/${formatInt(285)})`), + effect: 285 + }, + { + id: 121, + cost: 9, + STCost: 2, + requirement: [111], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [122, 123], + description: () => (Perk.studyActiveEP.isBought + ? `You gain ${formatX(50)} more Eternity Points` + : `You gain more EP based on how fast your last ten Eternities + were${PlayerProgress.realityUnlocked() ? " (real time)" : ""}`), + effect: () => (Perk.studyActiveEP.isBought + ? 50 + : Math.clamp(250 / Player.averageRealTimePerEternity, 1, 50)), + formatEffect: value => (Perk.studyActiveEP.isBought ? undefined : formatX(value, 1, 1)), + cap: 50 + }, + { + id: 122, + cost: 9, + STCost: 2, + requirement: [111], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [121, 123], + description: () => (Perk.studyPassive.isBought + ? `You gain ${formatX(50)} more Eternity Points` + : `You gain ${formatX(35)} more Eternity Points`), + effect: () => (Perk.studyPassive.isBought ? 50 : 35) + }, + { + id: 123, + cost: 9, + STCost: 2, + requirement: [111], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [121, 122], + description: "You gain more Eternity Points based on time spent this Eternity", + effect: () => { + const perkEffect = TimeSpan.fromMinutes(Perk.studyIdleEP.effectOrDefault(0)); + const totalSeconds = Time.thisEternity.plus(perkEffect).totalSeconds; + return Math.sqrt(1.39 * totalSeconds); }, - { - id: 22, - cost: 2, - requirement: [11], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: () => `Replicanti interval limit ${formatInt(50)}ms ➜ ${formatInt(1)}ms`, - effect: 1 + formatEffect: value => formatX(value, 1, 1) + }, + { + id: 131, + cost: 5, + STCost: 8, + requirement: [121], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [132, 133], + description: () => (Achievement(138).isUnlocked + ? `You can get ${formatPercents(0.5)} more Replicanti Galaxies` + : `Automatic Replicanti Galaxies are disabled, but you can get ${formatPercents(0.5)} more`), + effect: () => Math.floor(player.replicanti.boughtGalaxyCap / 2) + }, + { + id: 132, + cost: 5, + STCost: 8, + requirement: [122], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [131, 133], + description: () => (Perk.studyPassive.isBought && !Pelle.isDoomed + ? `Replicanti Galaxies are ${formatPercents(0.4)} stronger and Replicanti are ${format(3)} times faster` + : `Replicanti Galaxies are ${formatPercents(0.4)} stronger`), + effect: 0.4 + }, + { + id: 133, + cost: 5, + STCost: 8, + requirement: [123], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [131, 132], + description: () => (Achievement(138).isUnlocked + ? `Replicanti Galaxies are ${formatPercents(0.5)} stronger` + : `Replicanti are ${formatX(10)} slower until ${format(Number.MAX_VALUE, 2)}` + + `, but Replicanti Galaxies are ${formatPercents(0.5)} stronger`), + effect: 0.5 + }, + { + id: 141, + cost: 4, + STCost: 2, + requirement: [131], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [142, 143], + description: () => (Perk.studyActiveEP.isBought + ? `You gain ${formatX(DC.E45)} more Infinity Points` + : "Multiplier to Infinity Points, which decays over this Infinity"), + effect: () => (Perk.studyActiveEP.isBought + ? DC.E45 + : DC.E45.divide(thisInfinityMult(Time.thisInfinity.totalSeconds)).clampMin(1)), + formatEffect: value => (Perk.studyActiveEP.isBought ? undefined : formatX(value, 2, 1)) + }, + { + id: 142, + cost: 4, + STCost: 2, + requirement: [132], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [141, 143], + description: () => `You gain ${formatX(passiveIPMult())} more Infinity Points`, + effect: passiveIPMult, + cap: () => (Effarig.eternityCap === undefined ? undefined : Effarig.eternityCap.toNumber()) + }, + { + id: 143, + cost: 4, + STCost: 2, + requirement: [133], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [141, 142], + description: "Multiplier to Infinity Points, which increases over this Infinity", + effect: () => { + const perkEffect = TimeSpan.fromMinutes(Perk.studyIdleEP.effectOrDefault(0)); + const totalSeconds = Time.thisInfinity.plus(perkEffect).totalSeconds; + return thisInfinityMult(totalSeconds); }, - { - id: 31, - cost: 3, - requirement: [21], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: () => `Powers up bonuses that are based on your Infinities (Bonuses${formatPow(4)})`, - effect: 4 + formatEffect: value => formatX(value, 2, 1), + cap: () => Effarig.eternityCap + }, + { + id: 151, + cost: 8, + requirement: [141, 142, 143], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: () => `${formatX(1e4)} multiplier on all Time Dimensions`, + effect: 1e4 + }, + { + id: 161, + cost: 7, + requirement: [151], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: () => `${formatX(DC.E616)} multiplier on all Antimatter Dimensions`, + effect: () => DC.E616 + }, + { + id: 162, + cost: 7, + requirement: [151], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: () => `${formatX(1e11)} multiplier on all Infinity Dimensions`, + effect: 1e11 + }, + { + id: 171, + cost: 15, + requirement: [161, 162], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: () => `Time Shard requirement for the next Tickspeed upgrade goes up slower + ${formatX(1.33, 0, 2)} ➜ ${formatX(1.25, 0, 2)}`, + effect: () => TS171_MULTIPLIER + }, + { + id: 181, + cost: 200, + requirement: [171, + () => EternityChallenge(1).completions > 0 || Perk.bypassEC1Lock.isBought, + () => EternityChallenge(2).completions > 0 || Perk.bypassEC2Lock.isBought, + () => EternityChallenge(3).completions > 0 || Perk.bypassEC3Lock.isBought], + reqType: TS_REQUIREMENT_TYPE.ALL, + description: () => `You gain ${formatPercents(0.01)} of your Infinity Points gained on crunch each second`, + effect: () => gainedInfinityPoints().times(Time.deltaTime / 100) + .timesEffectOf(Ra.unlocks.continuousTTBoost.effects.autoPrestige) + }, + { + id: 191, + cost: 400, + requirement: [181, () => EternityChallenge(10).completions > 0], + reqType: TS_REQUIREMENT_TYPE.ALL, + description: () => `After Eternity you permanently keep ${formatPercents(0.05)} + of your Infinities as Banked Infinities`, + effect: () => Currency.infinities.value.times(0.05).floor() + }, + { + id: 192, + cost: 730, + requirement: [181, () => EternityChallenge(10).completions > 0, () => !Enslaved.isRunning], + reqType: TS_REQUIREMENT_TYPE.ALL, + description: () => (Enslaved.isRunning + ? "There is not enough space in this Reality" + : `Replicanti can go beyond ${format(replicantiCap(), 2, 1)}, but growth slows down at higher amounts`) + }, + { + id: 193, + cost: 300, + requirement: [181, () => EternityChallenge(10).completions > 0], + reqType: TS_REQUIREMENT_TYPE.ALL, + description: "Antimatter Dimension multiplier based on Eternities", + effect: () => (DC.E13000.pow(Currency.eternities.value.div(1e6).clampMax(1))), + cap: DC.E13000, + formatEffect: value => formatX(value, 2, 1) + }, + { + id: 201, + cost: 900, + requirement: [192], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: "Pick a second path from the Dimension Split" + }, + { + id: 211, + cost: 120, + requirement: [191], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: () => `Dimension Boost requirement scaling is reduced by ${formatInt(5)}`, + effect: 5 + }, + { + id: 212, + cost: 150, + requirement: [191], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: "All Galaxies are stronger based on your Time Shards", + effect: () => Math.pow(Currency.timeShards.value.clampMin(2).log2(), 0.005), + cap: 1.1, + formatEffect: value => `+${formatPercents(value - 1, 3)}` + }, + { + id: 213, + cost: 200, + requirement: [193], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: () => `You gain Replicanti ${formatInt(20)} times faster`, + effect: 20 + }, + { + id: 214, + cost: 120, + requirement: [193], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + description: "Dimensional Sacrifice boosts the 8th Antimatter Dimension even more", + effect: () => { + const totalBoost = Sacrifice.totalBoost; + const firstPart = totalBoost.pow(7.6).clampMaxExponent(44000); + const secondPart = totalBoost.pow(1.05).clampMaxExponent(120000); + return firstPart.times(secondPart); }, - { - id: 32, - cost: 2, - requirement: [22], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: `You gain more Infinities based on Dimension Boosts`, - effect: () => Math.max(DimBoost.totalBoosts, 1), - formatEffect: value => formatX(value) + cap: DC.E164000, + formatEffect: value => formatX(value, 2, 1) + }, + { + id: 221, + cost: 900, + STCost: 4, + requirement: [211], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [222], + description: "Time Dimension multiplier based on Dimension Boosts", + effect: () => DC.D1_0025.pow(DimBoost.totalBoosts), + formatEffect: value => formatX(value, 2, 1) + }, + { + id: 222, + cost: 900, + STCost: 4, + requirement: [211], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [221], + description: () => `Dimension Boost costs scale by another ${formatInt(2)} less`, + effect: 2 + }, + { + id: 223, + cost: 900, + STCost: 4, + requirement: [212], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [224], + description: () => `Distant Galaxy cost scaling starts ${formatInt(7)} Galaxies later`, + effect: 7 + }, + { + id: 224, + cost: 900, + STCost: 4, + requirement: [212], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [223], + description() { + const effect = TimeStudy(224).effectValue; + return `Distant Galaxy cost scaling starts ${quantifyInt("Galaxy", effect)} later + (${formatInt(1)} per ${formatInt(2000)} Dim Boosts)`; }, - { - id: 33, - cost: 2, - requirement: [21], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: "You keep half of your Replicanti Galaxies on Infinity" - }, - { - id: 41, - cost: 4, - requirement: [31], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: () => `All Galaxies give a ${formatX(DC.D1_2, 1, 1)} multiplier to Infinity Points gained`, - effect: () => DC.D1_2.pow(Replicanti.galaxies.total + player.galaxies + player.dilation.totalTachyonGalaxies), - formatEffect: value => formatX(value, 2, 1) - }, - { - id: 42, - cost: 6, - requirement: [32], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: () => `Antimatter Galaxy requirement increases by ${formatInt(52)} - 8th Dimensions instead of ${formatInt(60)}`, - effect: 52 - }, - { - id: 51, - cost: 3, - requirement: [41, 42], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: () => `You gain ${formatX(1e15)} more Infinity Points`, - effect: 1e15 - }, - { - id: 61, - cost: 3, - requirement: [51], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: () => `You gain ${formatX(15)} more Eternity Points`, - effect: 15 - }, - { - id: 62, - cost: 3, - requirement: [42, () => Perk.bypassEC5Lock.isBought || EternityChallenge(5).completions > 0], - reqType: TS_REQUIREMENT_TYPE.ALL, - description: () => `You gain Replicanti ${formatInt(3)} times faster`, - effect: 3 - }, - { - id: 71, - cost: 4, - requirement: [61, () => Perk.studyECRequirement.isBought || !EternityChallenge(12).isUnlocked], - reqType: TS_REQUIREMENT_TYPE.DIMENSION_PATH, - description: "Dimensional Sacrifice affects all other Antimatter Dimensions with reduced effect", - effect: () => Sacrifice.totalBoost.pow(0.25).clampMin(1), - cap: DC.E210000, - formatEffect: value => formatX(value, 2, 1) - }, - { - id: 72, - cost: 6, - requirement: [61, - () => Perk.studyECRequirement.isBought || - (!EternityChallenge(11).isUnlocked && !EternityChallenge(12).isUnlocked)], - reqType: TS_REQUIREMENT_TYPE.DIMENSION_PATH, - description: "Dimensional Sacrifice affects 4th Infinity Dimension with greatly reduced effect", - effect: () => Sacrifice.totalBoost.pow(0.04).clampMin(1), - cap: DC.E30000, - formatEffect: value => formatX(value, 2, 1) - }, - { - id: 73, - cost: 5, - requirement: [61, () => Perk.studyECRequirement.isBought || !EternityChallenge(11).isUnlocked], - reqType: TS_REQUIREMENT_TYPE.DIMENSION_PATH, - description: "Dimensional Sacrifice affects 3rd Time Dimension with greatly reduced effect", - effect: () => Sacrifice.totalBoost.pow(0.005).clampMin(1), - cap: DC.E1300, - formatEffect: value => formatX(value, 2, 1) - }, - { - id: 81, - cost: 4, - requirement: [71], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: () => `Base Dimension Boost power becomes ${formatX(10)}`, - effect: 10 - }, - { - id: 82, - cost: 6, - requirement: [72], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: "Dimension Boosts affect Infinity Dimensions", - effect: () => DC.D1_0000109.pow(Math.pow(DimBoost.totalBoosts, 2)), - cap: DC.E1E7, - formatEffect: value => formatX(value, 2, 1) - }, - { - id: 83, - cost: 5, - requirement: [73], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: "Dimension Boost multiplier based on tick upgrades gained from TDs", - effect: () => DC.D1_0004.pow(player.totalTickGained), - cap: DC.E30, - formatEffect: value => formatX(value, 2, 1) - }, - { - id: 91, - cost: 4, - requirement: [81], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: "Antimatter Dimension multiplier based on time spent in this Eternity", - effect: () => Decimal.pow10(Math.min(Time.thisEternity.totalMinutes, 20) * 15), - cap: DC.E300, - formatEffect: value => formatX(value, 2, 1) - }, - { - id: 92, - cost: 5, - requirement: [82], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: "Infinity Dimension multiplier based on fastest Eternity time", - effect: () => DC.D2.pow(60 / Math.max(Time.bestEternity.totalSeconds, 2)), - cap: DC.C2P30, - formatEffect: value => formatX(value, 2, 1) - }, - { - id: 93, - cost: 7, - requirement: [83], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: "Time Dimension multiplier based on tick upgrades gained", - effect: () => Decimal.pow(player.totalTickGained, 0.25).clampMin(1), - formatEffect: value => formatX(value, 2, 1) - }, - { - id: 101, - cost: 4, - requirement: [91], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: "Antimatter Dimension multiplier equal to Replicanti amount", - effect: () => Decimal.max(Replicanti.amount, 1), - formatEffect: value => formatX(value, 2, 1) - }, - { - id: 102, - cost: 6, - requirement: [92], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: "Replicanti Galaxies boost Replicanti multiplier", - effect: () => DC.D5.pow(player.replicanti.galaxies), - formatEffect: value => formatX(value, 2, 1) - }, - { - id: 103, - cost: 6, - requirement: [93], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: "Time Dimension multiplier equal to Replicanti Galaxy amount", - effect: () => Math.max(player.replicanti.galaxies, 1), - formatEffect: value => formatX(value, 2, 0) - }, - { - id: 111, - cost: 12, - requirement: [101, 102, 103], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: () => (Achievement(103).canBeApplied - ? `Make the Infinity Point formula better log(x/${formatFloat(307.8, 1)}) ➜ log(x/${formatInt(285)})` - : `Make the Infinity Point formula better log(x/${formatInt(308)}) ➜ log(x/${formatInt(285)})`), - effect: 285 - }, - { - id: 121, - cost: 9, - STCost: 2, - requirement: [111], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [122, 123], - description: () => (Perk.studyActiveEP.isBought - ? `You gain ${formatX(50)} more Eternity Points` - : `You gain more EP based on how fast your last ten Eternities - were${PlayerProgress.realityUnlocked() ? " (real time)" : ""}`), - effect: () => (Perk.studyActiveEP.isBought - ? 50 - : Math.clamp(250 / Player.averageRealTimePerEternity, 1, 50)), - formatEffect: value => (Perk.studyActiveEP.isBought ? undefined : formatX(value, 1, 1)), - cap: 50 - }, - { - id: 122, - cost: 9, - STCost: 2, - requirement: [111], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [121, 123], - description: () => (Perk.studyPassive.isBought - ? `You gain ${formatX(50)} more Eternity Points` - : `You gain ${formatX(35)} more Eternity Points`), - effect: () => (Perk.studyPassive.isBought ? 50 : 35) - }, - { - id: 123, - cost: 9, - STCost: 2, - requirement: [111], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [121, 122], - description: "You gain more Eternity Points based on time spent this Eternity", - effect: () => { - const perkEffect = TimeSpan.fromMinutes(Perk.studyIdleEP.effectOrDefault(0)); - const totalSeconds = Time.thisEternity.plus(perkEffect).totalSeconds; - return Math.sqrt(1.39 * totalSeconds); - }, - formatEffect: value => formatX(value, 1, 1) - }, - { - id: 131, - cost: 5, - STCost: 8, - requirement: [121], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [132, 133], - description: () => (Achievement(138).isUnlocked - ? `You can get ${formatPercents(0.5)} more Replicanti Galaxies` - : `Automatic Replicanti Galaxies are disabled, but you can get ${formatPercents(0.5)} more`), - effect: () => Math.floor(player.replicanti.boughtGalaxyCap / 2) - }, - { - id: 132, - cost: 5, - STCost: 8, - requirement: [122], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [131, 133], - description: () => (Perk.studyPassive.isBought && !Pelle.isDoomed - ? `Replicanti Galaxies are ${formatPercents(0.4)} stronger and Replicanti are ${format(3)} times faster` - : `Replicanti Galaxies are ${formatPercents(0.4)} stronger`), - effect: 0.4 - }, - { - id: 133, - cost: 5, - STCost: 8, - requirement: [123], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [131, 132], - description: () => (Achievement(138).isUnlocked - ? `Replicanti Galaxies are ${formatPercents(0.5)} stronger` - : `Replicanti are ${formatX(10)} slower until ${format(Number.MAX_VALUE, 2)}` + - `, but Replicanti Galaxies are ${formatPercents(0.5)} stronger`), - effect: 0.5 - }, - { - id: 141, - cost: 4, - STCost: 2, - requirement: [131], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [142, 143], - description: () => (Perk.studyActiveEP.isBought - ? `You gain ${formatX(DC.E45)} more Infinity Points` - : "Multiplier to Infinity Points, which decays over this Infinity"), - effect: () => (Perk.studyActiveEP.isBought - ? DC.E45 - : DC.E45.divide(thisInfinityMult(Time.thisInfinity.totalSeconds)).clampMin(1)), - formatEffect: value => (Perk.studyActiveEP.isBought ? undefined : formatX(value, 2, 1)) - }, - { - id: 142, - cost: 4, - STCost: 2, - requirement: [132], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [141, 143], - description: () => `You gain ${formatX(passiveIPMult())} more Infinity Points`, - effect: passiveIPMult, - cap: () => (Effarig.eternityCap === undefined ? undefined : Effarig.eternityCap.toNumber()) - }, - { - id: 143, - cost: 4, - STCost: 2, - requirement: [133], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [141, 142], - description: "Multiplier to Infinity Points, which increases over this Infinity", - effect: () => { - const perkEffect = TimeSpan.fromMinutes(Perk.studyIdleEP.effectOrDefault(0)); - const totalSeconds = Time.thisInfinity.plus(perkEffect).totalSeconds; - return thisInfinityMult(totalSeconds); - }, - formatEffect: value => formatX(value, 2, 1), - cap: () => Effarig.eternityCap - }, - { - id: 151, - cost: 8, - requirement: [141, 142, 143], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: () => `${formatX(1e4)} multiplier on all Time Dimensions`, - effect: 1e4 - }, - { - id: 161, - cost: 7, - requirement: [151], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: () => `${formatX(DC.E616)} multiplier on all Antimatter Dimensions`, - effect: () => DC.E616 - }, - { - id: 162, - cost: 7, - requirement: [151], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: () => `${formatX(1e11)} multiplier on all Infinity Dimensions`, - effect: 1e11 - }, - { - id: 171, - cost: 15, - requirement: [161, 162], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: () => `Time Shard requirement for the next Tickspeed upgrade goes up slower - ${formatX(1.33, 0, 2)} ➜ ${formatX(1.25, 0, 2)}`, - effect: () => TS171_MULTIPLIER - }, - { - id: 181, - cost: 200, - requirement: [171, - () => EternityChallenge(1).completions > 0 || Perk.bypassEC1Lock.isBought, - () => EternityChallenge(2).completions > 0 || Perk.bypassEC2Lock.isBought, - () => EternityChallenge(3).completions > 0 || Perk.bypassEC3Lock.isBought], - reqType: TS_REQUIREMENT_TYPE.ALL, - description: () => `You gain ${formatPercents(0.01)} of your Infinity Points gained on crunch each second`, - effect: () => gainedInfinityPoints().times(Time.deltaTime / 100).times(RA_UNLOCKS.TT_BOOST.effect.autoPrestige()) - }, - { - id: 191, - cost: 400, - requirement: [181, () => EternityChallenge(10).completions > 0], - reqType: TS_REQUIREMENT_TYPE.ALL, - description: () => `After Eternity you permanently keep ${formatPercents(0.05)} - of your Infinities as Banked Infinities`, - effect: () => Currency.infinities.value.times(0.05).floor() - }, - { - id: 192, - cost: 730, - requirement: [181, () => EternityChallenge(10).completions > 0, () => !Enslaved.isRunning], - reqType: TS_REQUIREMENT_TYPE.ALL, - description: () => (Enslaved.isRunning - ? "There is not enough space in this Reality" - : `Replicanti can go beyond ${format(replicantiCap(), 2, 1)}, but growth slows down at higher amounts`) - }, - { - id: 193, - cost: 300, - requirement: [181, () => EternityChallenge(10).completions > 0], - reqType: TS_REQUIREMENT_TYPE.ALL, - description: "Antimatter Dimension multiplier based on Eternities", - effect: () => (DC.E13000.pow(Currency.eternities.value.div(1e6).clampMax(1))), - cap: DC.E13000, - formatEffect: value => formatX(value, 2, 1) - }, - { - id: 201, - cost: 900, - requirement: [192], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: "Pick a second path from the Dimension Split" - }, - { - id: 211, - cost: 120, - requirement: [191], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: () => `Dimension Boost requirement scaling is reduced by ${formatInt(5)}`, - effect: 5 - }, - { - id: 212, - cost: 150, - requirement: [191], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: "All Galaxies are stronger based on your Time Shards", - effect: () => Math.pow(Currency.timeShards.value.clampMin(2).log2(), 0.005), - cap: 1.1, - formatEffect: value => `+${formatPercents(value - 1, 3)}` - }, - { - id: 213, - cost: 200, - requirement: [193], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: () => `You gain Replicanti ${formatInt(20)} times faster`, - effect: 20 - }, - { - id: 214, - cost: 120, - requirement: [193], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - description: "Dimensional Sacrifice boosts the 8th Antimatter Dimension even more", - effect: () => { - const totalBoost = Sacrifice.totalBoost; - const firstPart = totalBoost.pow(7.6).clampMaxExponent(44000); - const secondPart = totalBoost.pow(1.05).clampMaxExponent(120000); - return firstPart.times(secondPart); - }, - cap: DC.E164000, - formatEffect: value => formatX(value, 2, 1) - }, - { - id: 221, - cost: 900, - STCost: 4, - requirement: [211], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [222], - description: "Time Dimension multiplier based on Dimension Boosts", - effect: () => DC.D1_0025.pow(DimBoost.totalBoosts), - formatEffect: value => formatX(value, 2, 1) - }, - { - id: 222, - cost: 900, - STCost: 4, - requirement: [211], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [221], - description: () => `Dimension Boost costs scale by another ${formatInt(2)} less`, - effect: 2 - }, - { - id: 223, - cost: 900, - STCost: 4, - requirement: [212], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [224], - description: () => `Distant Galaxy cost scaling starts ${formatInt(7)} Galaxies later`, - effect: 7 - }, - { - id: 224, - cost: 900, - STCost: 4, - requirement: [212], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [223], - description() { - const effect = TimeStudy(224).effectValue; - return `Distant Galaxy cost scaling starts ${quantifyInt("Galaxy", effect)} later - (${formatInt(1)} per ${formatInt(2000)} Dim Boosts)`; - }, - effect: () => Math.floor(DimBoost.totalBoosts / 2000) - }, - { - id: 225, - cost: 900, - STCost: 4, - requirement: [213], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [226], - description: "You gain extra Replicanti Galaxies based on your Replicanti amount", - effect: () => Math.floor(Replicanti.amount.exponent / 1000), - formatEffect: value => `+${quantifyInt("RG", value)}` - }, - { - id: 226, - cost: 900, - STCost: 4, - requirement: [213], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [225], - description: "You gain extra Replicanti Galaxies based on their max", - effect: () => Math.floor(player.replicanti.boughtGalaxyCap / 15), - formatEffect: value => `+${quantifyInt("RG", value)}` - }, - { - id: 227, - cost: 900, - STCost: 4, - requirement: [214], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [228], - description: "Dimensional Sacrifice affects 4th Time Dimension with reduced effect", - effect: () => Math.max(Math.pow(Sacrifice.totalBoost.pLog10(), 10), 1), - formatEffect: value => formatX(value, 2, 2) - }, - { - id: 228, - cost: 900, - STCost: 4, - requirement: [214], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [227], - description: () => `Dimensional Sacrifice formula scales better - ${Sacrifice.getSacrificeDescription({ "TimeStudy228": false })} ➜ - ${Sacrifice.getSacrificeDescription({ "TimeStudy228": true })}`, - effect: 0.2 - }, - { - id: 231, - cost: 500, - STCost: 5, - requirement: [221, 222], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [232], - description: "Dimension Boosts are stronger based on their amount", - effect: () => Decimal.pow(DimBoost.totalBoosts, 0.3).clampMin(1), - formatEffect: value => formatX(value, 2, 2) - }, - { - id: 232, - cost: 500, - STCost: 5, - requirement: [223, 224], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [231], - description: "All Galaxies are stronger based on Antimatter Galaxies", - effect: () => Math.pow(1 + player.galaxies / 1000, 0.2), - formatEffect: value => `+${formatPercents(value - 1, 3)}` - }, - { - id: 233, - cost: 500, - STCost: 5, - requirement: [225, 226], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [234], - description: "Max Replicanti Galaxy upgrade is cheaper based on current Replicanti", - effect: () => Replicanti.amount.pow(0.3), - formatEffect: value => `/ ${format(value, 1, 2)}` - }, - { - id: 234, - cost: 500, - STCost: 5, - requirement: [227, 228], - reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, - requiresST: [233], - description: "Dimensional Sacrifice applies to 1st Antimatter Dimension", - effect: () => Sacrifice.totalBoost, - }, - // Note: These last 4 entries are the triad studies - { - id: 301, - cost: 0, - STCost: 12, - requirement: [() => Ra.pets.v.level >= 5, 221, 222, 231], - reqType: TS_REQUIREMENT_TYPE.ALL, - requiresST: [221, 222, 231], - description: "Time Study 231 improves the effect of Time Study 221", - effect: () => TimeStudy(221).effectValue.pow(TimeStudy(231).effectValue.minus(1)).clampMin(1), - formatEffect: value => formatX(value, 2, 1), - unlocked: () => Ra.pets.v.level >= 5 - }, - { - id: 302, - cost: 0, - STCost: 12, - requirement: [() => Ra.pets.v.level >= 10, 223, 224, 232], - reqType: TS_REQUIREMENT_TYPE.ALL, - requiresST: [223, 224, 232], - description: () => `Distant Galaxy scaling threshold starts another ${formatInt(3000)} Antimatter Galaxies later`, - effect: 3000, - unlocked: () => Ra.pets.v.level >= 10 - }, - { - id: 303, - cost: 0, - STCost: 12, - requirement: [() => Ra.pets.v.level >= 15, 225, 226, 233], - reqType: TS_REQUIREMENT_TYPE.ALL, - requiresST: [225, 226, 233], - description: () => `Gain ${formatPercents(0.5)} more extra Replicanti Galaxies from Time Studies 225 and 226, - and from Effarig's Infinity`, - effect: 1.5, - unlocked: () => Ra.pets.v.level >= 15 - }, - { - id: 304, - cost: 0, - STCost: 12, - requirement: [() => Ra.pets.v.level >= 20, 227, 228, 234], - reqType: TS_REQUIREMENT_TYPE.ALL, - requiresST: [227, 228, 234], - description: "Dimensional Sacrifice multiplier is squared", - effect: 2, - unlocked: () => Ra.pets.v.level >= 20 - } - ]; -}()); + effect: () => Math.floor(DimBoost.totalBoosts / 2000) + }, + { + id: 225, + cost: 900, + STCost: 4, + requirement: [213], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [226], + description: "You gain extra Replicanti Galaxies based on your Replicanti amount", + effect: () => Math.floor(Replicanti.amount.exponent / 1000), + formatEffect: value => `+${quantifyInt("RG", value)}` + }, + { + id: 226, + cost: 900, + STCost: 4, + requirement: [213], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [225], + description: "You gain extra Replicanti Galaxies based on their max", + effect: () => Math.floor(player.replicanti.boughtGalaxyCap / 15), + formatEffect: value => `+${quantifyInt("RG", value)}` + }, + { + id: 227, + cost: 900, + STCost: 4, + requirement: [214], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [228], + description: "Dimensional Sacrifice affects 4th Time Dimension with reduced effect", + effect: () => Math.max(Math.pow(Sacrifice.totalBoost.pLog10(), 10), 1), + formatEffect: value => formatX(value, 2, 2) + }, + { + id: 228, + cost: 900, + STCost: 4, + requirement: [214], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [227], + description: () => `Dimensional Sacrifice formula scales better + ${Sacrifice.getSacrificeDescription({ "TimeStudy228": false })} ➜ + ${Sacrifice.getSacrificeDescription({ "TimeStudy228": true })}`, + effect: 0.2 + }, + { + id: 231, + cost: 500, + STCost: 5, + requirement: [221, 222], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [232], + description: "Dimension Boosts are stronger based on their amount", + effect: () => Decimal.pow(DimBoost.totalBoosts, 0.3).clampMin(1), + formatEffect: value => formatX(value, 2, 2) + }, + { + id: 232, + cost: 500, + STCost: 5, + requirement: [223, 224], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [231], + description: "All Galaxies are stronger based on Antimatter Galaxies", + effect: () => Math.pow(1 + player.galaxies / 1000, 0.2), + formatEffect: value => `+${formatPercents(value - 1, 3)}` + }, + { + id: 233, + cost: 500, + STCost: 5, + requirement: [225, 226], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [234], + description: "Max Replicanti Galaxy upgrade is cheaper based on current Replicanti", + effect: () => Replicanti.amount.pow(0.3), + formatEffect: value => `/ ${format(value, 1, 2)}` + }, + { + id: 234, + cost: 500, + STCost: 5, + requirement: [227, 228], + reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE, + requiresST: [233], + description: "Dimensional Sacrifice applies to 1st Antimatter Dimension", + effect: () => Sacrifice.totalBoost, + }, + // Note: These last 4 entries are the triad studies + { + id: 301, + cost: 0, + STCost: 12, + requirement: [() => Ra.unlocks.unlockHardV.effectOrDefault(0) >= 1, 221, 222, 231], + reqType: TS_REQUIREMENT_TYPE.ALL, + requiresST: [221, 222, 231], + description: "Time Study 231 improves the effect of Time Study 221", + effect: () => TimeStudy(221).effectValue.pow(TimeStudy(231).effectValue.minus(1)).clampMin(1), + formatEffect: value => formatX(value, 2, 1), + unlocked: () => Ra.unlocks.unlockHardV.effectOrDefault(0) >= 1 + }, + { + id: 302, + cost: 0, + STCost: 12, + requirement: [() => Ra.unlocks.unlockHardV.effectOrDefault(0) >= 2, 223, 224, 232], + reqType: TS_REQUIREMENT_TYPE.ALL, + requiresST: [223, 224, 232], + description: () => `Distant Galaxy scaling threshold starts another ${formatInt(3000)} Antimatter Galaxies later`, + effect: 3000, + unlocked: () => Ra.unlocks.unlockHardV.effectOrDefault(0) >= 2 + }, + { + id: 303, + cost: 0, + STCost: 12, + requirement: [() => Ra.unlocks.unlockHardV.effectOrDefault(0) >= 3, 225, 226, 233], + reqType: TS_REQUIREMENT_TYPE.ALL, + requiresST: [225, 226, 233], + description: () => `Gain ${formatPercents(0.5)} more extra Replicanti Galaxies from Time Studies 225 and 226, + and from Effarig's Infinity`, + effect: 1.5, + unlocked: () => Ra.unlocks.unlockHardV.effectOrDefault(0) >= 3 + }, + { + id: 304, + cost: 0, + STCost: 12, + requirement: [() => Ra.unlocks.unlockHardV.effectOrDefault(0) >= 4, 227, 228, 234], + reqType: TS_REQUIREMENT_TYPE.ALL, + requiresST: [227, 228, 234], + description: "Dimensional Sacrifice multiplier is squared", + effect: 2, + unlocked: () => Ra.unlocks.unlockHardV.effectOrDefault(0) >= 4 + } +]; diff --git a/javascripts/core/secret-formula/game-database.js b/javascripts/core/secret-formula/game-database.js index 3bda43728..9d66c80e5 100644 --- a/javascripts/core/secret-formula/game-database.js +++ b/javascripts/core/secret-formula/game-database.js @@ -10,8 +10,11 @@ export const GameDatabase = { glyphSacrifice: {}, }, celestials: { + effarig: {}, + alchemy: {}, pelle: {}, descriptions: {}, + quotes: {}, } }; diff --git a/javascripts/core/secret-formula/h2p.js b/javascripts/core/secret-formula/h2p.js index fed3e1601..476976d5c 100644 --- a/javascripts/core/secret-formula/h2p.js +++ b/javascripts/core/secret-formula/h2p.js @@ -1,6 +1,6 @@ -import { GameDatabase } from "./game-database.js"; -import { DC } from "../constants.js"; -import { Pelle, PelleStrikes } from "../globals.js"; +import { DC } from "../constants"; + +import { GameDatabase } from "./game-database"; GameDatabase.h2p = { /** @@ -41,7 +41,7 @@ In addition to importing and exporting to your clipboard, you can also import an
You can use the "Choose save" button to pick between three separate saves on your browser. These saves are, for most intents and purposes, completely separate from each other. Importing and exporting will only affect the current save -slot. The only exception is clearing your broswer data, in which case all three saves will be reset. +slot. The only exception is clearing your broswer data, in which case all three saves will be reset.

The game automatically saves periodically, by default once every ${formatInt(30)} seconds, and it will notify you in @@ -103,10 +103,12 @@ complicated to be run at full accuracy in a reasonable amount of time. At the en summarize how various relevant resources have changed while you were gone.

-The game runs on a system where everything is updated once per tick - all dimensions and resources do one unit of +The game runs on a system where everything is updated once per tick - all Dimensions and resources do one unit of production, all autobuyers trigger once, all multipliers and values are changed accordingly, and all the displayed -numbers are updated. There are normally ${formatInt(20)} ticks per second when the game is running, although lag and -internal javascript behavior may cause tick length to vary by a few milliseconds. +numbers are updated. By default there are ${formatInt(20)} ticks per second when the game is running, although this can +be modified by changing the "Update rate" within the game Options. +Your current settings will run the game at ${format(1000 / player.options.updateRate, 2, 1)} ticks per second on +average, although lag and internal javascript behavior may cause individual ticks to vary by a few percent.

When offline simulation is active, these ticks have an adjusted length in order to fill the amount of time you were @@ -117,6 +119,17 @@ autobuyers - in this situation autobuyers will effectively only trigger once eve may have a strong impact depending on the part of the game.

+${BlackHole(1).isUnlocked + ? `Offline Black Hole behavior: Once the Black Hole has been unlocked, the offline progress simulation will + attempt to run the game in a way where each tick contains roughly the same amount of game time. This may + give the appearance of the Black Hole(s) being active for a much larger fraction of time than normal while + simulating, when in fact the game is running active periods more slowly and "skipping past" the inactive periods + because they contribute much less production per real time. This results in behavior which is generally in your + favor when compared to ticks with constant real time. +
+
` + : "" +} Offline tick count can be adjusted between ${formatInt(500)} and ${formatInt(DC.E6)} ticks. Smaller counts will result in faster but less accurate simulations, while larger counts will result in more accurate simulations which take longer to complete. @@ -130,8 +143,49 @@ the game closed. isUnlocked: () => true, tags: ["offline", "away", "progress"], tab: "options/gameplay" - }, - { + }, { + name: "Effect Stacking", + info: () => ` +Most of the effects and upgrades in Antimatter Dimensions largely fall into three categories: +
+- Additive: These effects are typically denoted with a + (or the word "increase") followed by a number, +and add their value to some +base amount. Multiple additive effects are summed up. These can also sometimes show up as subtractive effects which +reduce resource costs. +
+- Multiplicative: These effects are shown either by a × (or the word "multiply") followed by a number or, +more rarely, as two numbers +separated by a ➜. Different multiplicative sources always combine by multiplying, never by adding. In some situations, +there may be negative effects or cost reductions that apply in this category as division. +
+- Power: These effects are much rarer and appear as ^ followed by a number. Multiple power effects apply +sequentially, or equivalently by multiplying the values of the power effects together and applying the final value +as a single power. In rare situations, negative effects may apply here in this category as powers which are less +than ${formatInt(1)}. +
+
+Unless otherwise noted when an upgrade or reward replaces an older value, all of these effects stack +with each other. In the case of an upgrade replacing an older value with a newer value, the replacement occurs before +any of the above effects are applied. To determine the final value of a set of effects, the effects from each category +are individually combined, and then applied in the order of additive, multiplicative, then power effects. +
+
+${PlayerProgress.realityUnlocked() || PlayerProgress.dilationUnlocked() + ? "Dilation and any Dilation-like effects apply after all of these other effects are stacked together." + : ""} +
+
+${PlayerProgress.realityUnlocked() + ? `Glyph Effects effectively have two stacking attributes; their internal way of stacking together and the way + they stack with all other game effects. These may not necessarily be the same - for example, the "Antimatter + Dimension Power" effect will stack additively with itself, but then the total effect will be added to + a base value of ${formatInt(1)} and then applied as a power effect to Antimatter Dimensions.` + : ""} +`, + isUnlocked: () => true, + tags: ["effect", "stack", "combine", "add", "reduce", "multiply", "divide", "power", "dilation", "glyph"], + tab: "options/gameplay" + }, { name: "Antimatter Dimensions", info: () => ` Antimatter is a resource that is throughout the entire game for purchasing various things as you progress. You start @@ -139,12 +193,12 @@ with ${formatInt(10)} antimatter when you first open the game. And you can spend it to buy the 1st Antimatter Dimension to start the game.

-Antimatter Dimensions are your production units in game. The first Antimatter Dimension produces your antimatter. +Antimatter Dimensions are your production units in game. The 1st Antimatter Dimension produces your antimatter. Each consecutive Antimatter Dimension produces the previous one, allowing you to have steady growth. There are eight Antimatter Dimensions total.

-Dimension Multiplier: Beside the Dimension there is a multiplier (example: First Dimension ${formatX(1, 1, 1)}). +Dimension Multiplier: Beside the Dimension there is a multiplier (example: 1st Dimension ${formatX(1, 1, 1)}). The base production of each Dimension is multiplied by this number. This multiplier increases by ${formatX(2)} for every ${formatInt(10)} of that Dimension purchased. Each time this occurs, the price of the dimension will increase. @@ -172,7 +226,7 @@ Alternatively, if the Until ${formatInt(10)} button is highlighted, you can buy whatever quantity gets you to that Dimension's next Dimension multiplier.

-Max all: Max all will buy until ${formatInt(10)} of the first Antimatter Dimension until it can't anymore, +Max all: Max all will buy until ${formatInt(10)} of the 1st Antimatter Dimension until it can't anymore, then second, and so on until the 8th Antimatter Dimension, and then buy max Tickspeed Upgrades.

@@ -195,12 +249,13 @@ ${formatInt(1)} instead of ${formatInt(10)}), M for Max all }, { name: "Tickspeed", info: () => ` -Production in the game happens on each "tick", which initially occurs once per second. By buying Tickspeed upgrades, +Production in the game happens on each "tick", which initially occurs once per second. By buying Tickspeed Upgrades, you can make your Antimatter Dimensions produce faster, as if multiple ticks occur in each second.

Tickspeed: This states how many game ticks are occurring every second. Fractional ticks are accounted for, -boosting production as if part of a game tick has passed. +boosting production as if part of a game tick has passed. Note that the actual tickspeed time is simulated and the +game always runs calculations at the update rate you've chosen in the Options tab.

Cost: The cost of antimatter for multiplying ticks/sec by the displayed multiplier. @@ -212,8 +267,7 @@ with your current amount of antimatter.

Hotkeys: T will purchase as many Tickspeed Upgrades as possible, or Shift+T to buy a single upgrade. -Note that the actual tickspeed time is simulated and the game always runs calculations at the update rate you've chosen -in the Options tab. +M for Max all. `, isUnlocked: () => Tickspeed.isUnlocked, tags: ["dimension", "earlygame", "time"], @@ -221,7 +275,7 @@ in the Options tab. }, { name: "Dimension Boosts", info: () => ` -Dimension Boost: This resets your Antimatter and all of your Antimatter Dimensions, but unlocks another +Dimension Boost: This resets your antimatter and all of your Antimatter Dimensions, but unlocks another Antimatter Dimension for you to purchase and boosts your Dimension multipliers. The 1st Dimension Boost requires ${formatInt(20)} 4th Dimensions, the 2nd requires ${formatInt(20)} 5th Dimensions, etc. After unlocking all ${formatInt(8)} Dimensions, @@ -245,7 +299,7 @@ the 3rd Dimension ${formatX(2)}, and all other Dimensions are unaffected. info: () => ` Purchasing an Antimatter Galaxy will reset your game back to the point where only ${formatInt(4)} Dimensions are available, but will increase the effect of your Tickspeed Upgrades by +${format(0.02, 0, 2)} for your first two -galaxies. As you get more galaxies, the multiplier will continue becoming stronger and stronger. +Galaxies. As you get more Galaxies, the multiplier will continue becoming stronger and stronger.

Though it will have very little impact for the first few purchases, @@ -255,10 +309,11 @@ the increase is multiplicative and won't take long to be visible. Your first Antimatter Galaxy requires ${formatInt(80)} Eighth Dimensions, and each additional Galaxy will cost another ${formatInt(60)} more.
-Distant Galaxy scaling: Above ${formatInt(100)} Antimatter Galaxies the cost increase between Galaxies will increase by -${formatInt(2)} per Galaxy, making the next Galaxy cost ${formatInt(62)} more, then ${formatInt(64)} more, etc. +Distant Galaxy scaling: Above ${formatInt(100)} Antimatter Galaxies the cost increase between Galaxies will +increase by ${formatInt(2)} per Galaxy, making the next Galaxy cost ${formatInt(62)} more, then ${formatInt(64)} more, +etc.
-Remote Galaxy scaling: Above ${formatInt(Galaxy.remoteStart)} Antimatter Galaxies, the total cost +Remote Galaxy scaling: Above ${formatInt(Galaxy.remoteStart)} Antimatter Galaxies, the total cost increases by another ${formatPercents(0.002, 1)} per Galaxy, on top of Distant scaling.

@@ -278,7 +333,7 @@ multiplier or the current cost. In return, it will multiply the Eighth Dimension It will take time to get back to the production you previously had, but you'll end up with a net increase.

-The Dimensional Sacrifice multiplier scales with the number of First Dimensions you had at the time of sacrifice, +The Dimensional Sacrifice multiplier scales with the number of 1st Dimensions you had at the time of sacrifice, and the scaling can be improved by completing certain Achievements and challenges. The multiplier is kept between sacrifices, meaning that sacrificing once at ${formatX(10)} and then once at ${formatX(4)} will be the same as ${formatX(8)} then ${formatX(5)}; in both cases you'll end up with a total sacrifice multiplier of ${formatX(40)}. @@ -293,7 +348,7 @@ ${formatX(8)} then ${formatX(5)}; in both cases you'll end up with a total sacri name: "Achievements", // This one could use some work! info: () => ` -Each Achievement has requirements to unlock. Once unlocked, some achievements give a reward. +Each Achievement has requirements to unlock. Once unlocked, some Achievements give a reward. Requirements and rewards vary in difficulty and benefit significantly.

@@ -308,7 +363,7 @@ well as an additional ${formatX(1.25, 2, 2)} for each fully completed row. info: () => ` Once you have too much antimatter for the world to handle (${formatInt(2)}${formatInt(1024)} or about ${formatPostBreak(Number.MAX_VALUE, 6)}, -sometimes called "Infinity"), you'll be forced to do a “Big Crunch”. This will reset your Antimatter, Antimatter +sometimes called "Infinity"), you'll be forced to do a “Big Crunch”. This will reset your antimatter, Antimatter Dimensions, Dimension Boosts, and your Antimatter Galaxies. Doing a Big Crunch is also sometimes referred to as "Infinitying".
@@ -381,7 +436,7 @@ This can be disabled. or until ${formatInt(10)}. Bulk buy is disabled when the autobuyer is set to singles.

-Tickspeed Autobuyer Buy Quantity: The Tickspeed autobuyer can be set to buy a single upgrade per activation +Tickspeed Autobuyer Buy Quantity: The tickspeed autobuyer can be set to buy a single upgrade per activation or to buy the max possible once the Tickspeed Challenge (C9) has been beaten.

@@ -400,11 +455,14 @@ If you reach your specified Galaxy threshold, the autobuyer will ignore your max Sacrifice Autobuyer: This autobuyer starts with a maxed interval, potentially triggering every tick.

-Toggle All Autobuyers: This button will turn all of your autobuyers on or off. This won't change your -individual autobuyer settings. +Pause/Resume Autobuyers: This button will pause or resume autobuyers which are turned on. +It does not change individual autobuyer settings. Think of it like a master switch.

-Hotkey: A (for toggle all autobuyers). +Enable/Disable All Autobuyers: This button will turn all of your autobuyers on or off individually. +
+
+Hotkey: A (for pausing/resuming autobuyers). Additionally, holding Alt when pressing a hotkey associated with an upgrade, dimension, or prestige will toggle the associated autobuyer. `, @@ -468,7 +526,7 @@ of Infinity Dimensions doesn't carry between crunches, all the multipliers you g .join(", ")}

-Instead of antimatter, the First Infinity Dimension produces Infinity Power, which gives a multiplier applied +Instead of antimatter, the 1st Infinity Dimension produces Infinity Power, which gives a multiplier applied to all Antimatter Dimensions equal to (power${formatInt(7)}). Infinity Dimensions are not affected by Tickspeed Upgrades. `, @@ -506,7 +564,8 @@ every Replicanti tick (initially every second), and both of these can be upgrade
If you have purchased a Replicanti Galaxy upgrade, then you can get a "free" Replicanti Galaxy in exchange for resetting your Replicanti count back to ${formatInt(1)}. This Galaxy is free in that it will act as if it was an -Antimatter Galaxy, but it won't make your next Antimatter Galaxy more expensive. +Antimatter Galaxy, but it won't make your next Antimatter Galaxy more expensive. However, it will still reset the same +things as an Antimatter Galaxy does.

Hotkey: R will try to purchase a Replicanti Galaxy. @@ -580,12 +639,12 @@ properly, as noted on the milestone page itself. name: "Time Dimensions", info: () => ` After your first Eternity, you unlock Time Dimensions. You buy them with Eternity Points and they produce Time Shards, -which provide Tickspeed upgrades. These Tickspeed upgrades function like normal Tickspeed upgrades but don't increase -their cost. Time Dimensions, Time Shards, and the Tickspeed upgrades they provide are kept on Infinity, +which provide Tickspeed Upgrades. These Tickspeed Upgrades function like normal Tickspeed Upgrades but don't increase +their cost. Time Dimensions, Time Shards, and the Tickspeed Upgrades they provide are kept on Infinity, but reset every Eternity.

-Similarly to the other dimensions, Second Time Dimensions produce First Time Dimensions and so on. Similarly to Infinity +Similarly to the other dimensions, Second Time Dimensions produce 1st Time Dimensions and so on. Similarly to Infinity Dimensions, your production will be reset to the amount you purchased after every Eternity, but you'll keep any upgrades to your multipliers you purchased.
@@ -608,7 +667,7 @@ purpose of cost increases, causing the price to rise much more steeply.

Each threshold to gain another Tickspeed Upgrade is ${formatPercents(0.33)} more Time Shards than the previous, -or ${formatPercents(0.25)} with the relevant time study. After ${formatInt(FreeTickspeed.softcap)} upgrades, the +or ${formatPercents(0.25)} with the relevant Time Study. After ${formatInt(FreeTickspeed.softcap)} upgrades, the multiplier between each successive free Tickspeed Upgrade will gradually increase at a rate of ~${formatX(1.35, 0, 2)} per ${formatInt(50000)} upgrades (${formatX(1.000006, 0, 6)} per upgrade). `, @@ -639,7 +698,7 @@ once in order to access the study. You don't need to have the challenge study pu Near the bottom, where all the edges join together again, you can only pick one study out of each pair.

-You are able to hold down shift and then click on a time study to buy all studies until that point. This might not buy +You are able to hold down shift and then click on a Time Study to buy all studies until that point. This might not buy the studies you want if you shift-click a study in a position where you would have to choose between two or more different options which you can't get together (see above), or you can't afford all the studies needed to reach that point. Shift-click will buy studies greedily, getting as many as possible per row before moving farther downward. @@ -663,7 +722,7 @@ to buy your preferred path and continue on instead of stopping completely at the for the Dimension split in this dialog if you have purchased the relevant Time Study.

-Respecs: A Respec allows you to reset the upgrades you have in the tree to retreive all of the Time Theorems +Respecs: A respec allows you to reset the upgrades you have in the tree to retreive all of the Time Theorems spent on them. It can be done for free, but only triggers on finishing an Eternity; you can't respec Time Studies in the middle of an Eternity.
@@ -735,7 +794,7 @@ of your next Antimatter Galaxy.

Unlocking Time Dilation also unlocks upgrades you can purchase using Dilated Time. The first and third upgrades in the -first row of Dilation upgrades can be repeatedly purchased as many times as you can afford them. The second upgrade can +first row of Dilation Upgrades can be repeatedly purchased as many times as you can afford them. The second upgrade can also be repeatedly bought, but eventually reaches a cap. `, isUnlocked: () => DilationTimeStudyState.studies[1].isBought || PlayerProgress.realityUnlocked(), @@ -759,12 +818,12 @@ header in the Statistics tab and all of your best Challenge times. You need to redo the requirements for each Achievement in order to get their rewards again, but you'll also passively unlock the next incomplete Achievement every ${timeDisplayNoDecimals(30 * 60000)} without any effort even if you otherwise don't have the requirements to do so. This automatic completion can be disabled, in which case the timer will -count down to zero and pause, immediately completing another Achievement immediately when unpaused. The timer still -progresses at the same rate while offline. +count down to zero and pause, immediately completing another Achievement when unpaused. The timer still progresses +at the same rate while offline.

Reality Machines can be spent on different upgrades throughout the Reality tab and are your primary currency from this -point onwards. Glyphs are equippable objects which you must equip in order to use their boosts. Perk points are another +point onwards. Glyphs are equippable objects which you must equip in order to use their boosts. Perk Points are another currency that can be spent in the Perks subtab on different Perks.

@@ -804,7 +863,7 @@ The percentage is effectively a quality rating, higher values are better. Specific ranges of rarities are given names, such as Common or Uncommon.
Effects - These are the boosts that equipping the Glyph will give you, and can contain up to four effects. -Stronger Glyphs will generally have more effects than weaker Glyphs. +Glyphs with higher level or rarity will generally have more effects than weaker Glyphs.
Note: Your first Glyph will have a fixed effect and rarity, but its level will scale based on your progress before any Reality content. Once you receive a Glyph, its attributes can't be changed. @@ -823,18 +882,18 @@ effects of the new Glyph. You can also drag Glyphs into already-occupied slots t but this will restart your current Reality.

-The slots in the first rows of your inventory are "protected" slots. New glyphs will never be placed into them (even if +The slots in the first rows of your inventory are "protected" slots. New Glyphs will never be placed into them (even if there is no more room in your inventory), and they are unaffected by the Sort and Auto clean buttons.

You can delete Glyphs from your inventory by shift-clicking them, which will prompt you with a confirmation dialog asking if you are sure you want to delete the Glyph. Holding shift and ctrl together while clicking will bypass this dialog. However, deleting Glyphs will give you no benefit beyond clearing up inventory space if you do so before -unlocking Glyph Sacrifice from a Reality upgrade! +unlocking Glyph Sacrifice from a Reality Upgrade!

Clicking a group of circular Glyphs outside of a modal window will open up a modal which displays a detailed summary -of all those glyphs and their various attributes. The summary will show the information for all Glyphs at once with +of all those Glyphs and their various attributes. The summary will show the information for all Glyphs at once with slightly shorter descriptions, making it more suitable for sharing with others. This can be done for Glyph records in the Statistics page, your equipped Glyphs, and the Upcoming Glyph Selection this Reality. `, @@ -855,14 +914,14 @@ to Perks you already have, although there are loops in the tree which you can go

The Perk nodes can have two different shapes - circular or diamond. The only difference between the two is that -diamond-shaped perks give Automator Points in addition to their normal effect. Different nodes also have +diamond-shaped Perks give Automator Points in addition to their normal effect. Different nodes also have different colors, roughly indicating which part of the game they affect the most. `, isUnlocked: () => PlayerProgress.realityUnlocked() || TimeStudy.reality.isBought, tags: ["pp", "reality", "tree", "endgame", "lategame"], tab: "reality/perks" }, { - name: "Automator", + name: "Automator Overview", info: () => ` The Automator is unlocked upon reaching a total of ${formatInt(AutomatorPoints.pointsForAutomator)} Automator Points. Automator Points are given when unlocking various Perks or Reality Upgrades, by unlocking the Black Hole, or by @@ -870,32 +929,8 @@ simply completing more Realities.

The Automator uses a scripting language that allows you to automate nearly the entire game. -The interface has two panes, a script pane on the left where you enter the commands to automate the game, and a -multiple function pane on the right. -
-These functions include: -
-- a brief introduction to the Automator -
-- the command list, with information on all the commands available to you -
-- the template creator, which allows you to fill in premade templates to suit your own purposes -
-- a list of all errors in the current Automator script -
-- a list of recently executed commands and what those commands did -
-- if you are in the Block mode of the Automator, the command blocks used to write the script -
-
-You can use as many rows as you need. -
-Some commands are gated behind unlocks, which will only become visible once you have unlocked them. -
-
-You are able to create new scripts by clicking on the dropdown, and then clicking the "Create New..." option. -To rename a script, click the pencil next to the dropdown. Scripts are automatically saved as you edit them. -You can create as many scripts as you want. +The interface has two panes, a script pane on the left where you enter the commands to automate the game and a pane +on the right which has multiple panels which do many different things as explained on the Automator Introduction page.

If you want a larger workspace, you can press the button in the top right corner of the documentation pane of the @@ -903,12 +938,12 @@ Automator to expand it to fullscreen. You can also drag the boundary between the panes if you want more room to write your script or read documentation.

-By pressing the top-right button on the script pane, you can switch to block mode, which may be more approachable if -you are unfamiliar with programming. To enter commands in block mode, drag the box for the relevant command from the -documentation pane into the script pane and drop it where you want the command to go. Commands can be freely -rearranged by dragging the blocks around if needed. Clicking the top-right button in block mode will switch back to -text mode, and switching between block and text mode will automatically translate your script as well. -Note that scripts can only be converted into block mode if they have no errors! +By pressing the top-right button on the script pane, you can switch between the Automator's block and text editor +modes; the block mode may be more approachable if you are unfamiliar with programming. To enter commands in block mode, +select the command block pane on the right and drag the box for the relevant command into the script pane and drop it +where you want the command to go. Commands can be freely rearranged by dragging the blocks around if needed. Switching +between block and text mode will attempt to automatically translate your script as well, although you may lose part of +your converted script if it contains errors.

Just like your entire savefile, individual Automator scripts can be imported and exported from the game. @@ -919,6 +954,62 @@ won't be lost or overwritten.

Hotkey: U will pause/unpause the Automator. +`, + isUnlocked: () => Player.automatorUnlocked, + tags: ["automation", "reality", "code", "script", "endgame", "lategame"], + tab: "automation/automator" + }, { + name: "Automator Technical Details", + info: () => ` +Technical Limits +
+
+There are a few limitations to scripts in order to reduce lag and prevent save file size from getting too large. +These limits are as follows: +
+- Individual scripts are limited to a maximum of ${formatInt(AutomatorData.MAX_ALLOWED_SCRIPT_CHARACTERS)} +characters each and all scripts combined together cannot exceed ${formatInt(AutomatorData.MAX_ALLOWED_TOTAL_CHARACTERS)} +characters total. +
+- Script names cannot exceed ${formatInt(AutomatorData.MAX_ALLOWED_SCRIPT_NAME_LENGTH)} characters. +
+- Defined constants cannot have names longer than ${formatInt(AutomatorData.MAX_ALLOWED_CONSTANT_NAME_LENGTH)} +characters, or values longer than ${formatInt(AutomatorData.MAX_ALLOWED_CONSTANT_VALUE_LENGTH)} characters. +
+- You cannot have more than a total of ${formatInt(AutomatorData.MAX_ALLOWED_SCRIPT_COUNT)} scripts or +${formatInt(AutomatorData.MAX_ALLOWED_CONSTANT_COUNT)} defined constants. +
+
+Script Saving +
+
+Scripts are automatically saved as you edit them, but are not saved to your game save until the global autosave timer +(ie. "Time since last save") triggers a full game save. If you make changes to scripts right before closing the game, +you should wait until the game saves afterwards in order to not lose your changes. Any edits made to your scripts +while above the length limits will not be saved until you shorten your scripts to be below them again. +
+
+Automator Ticks +
+
+The Automator's "execution timer" is based on real time, and is therefore unaffected by things such as the Black Hole, +Time Glyph effects, and EC12's negative effect. However this execution timer runs entirely independently from the main +game's production loop, meaning that at faster speeds the Automator can run multiple commands per production tick. +
+
+Some commands are more intensive on the game's internal code and may take longer than a single Automator tick in order +to process on slower computers. In that case, the Automator will execute those commands and then attempt to "catch up" +by executing the following commands as quickly as possible until it has run as many commands as it should have at a +constant execution speed. +
+
+Interactions with Offline Progress +
+
+Longer production ticks during Offline Progress simulation means that all of your resources are effectively given +in large chunks instead of more continuously. This may have potentially adverse effects on your script's +behavior while offline, depending on how exactly your script depends on the game state to work properly. +Additionally, the PAUSE command may behave oddly due to it also being based on real time. `, isUnlocked: () => Player.automatorUnlocked, tags: ["automation", "reality", "code", "script", "endgame", "lategame"], @@ -939,7 +1030,10 @@ and effects which are boosted purely on time spent (eg. idle path IP/EP multipli
While most features in the game are boosted by this increased game speed, there are some which remain unaffected. In these cases, it will be specifically mentioned that a given time is stated as real time as opposed to -game time. One such example is the set of perks which automatically completes Eternity Challenges over time. +game time. One such example is the set of Perks which automatically completes Eternity Challenges over time. +Otherwise, it should be assumed from this point onward that all references to time are for game time. +Note that this also includes situations where you may want to have a lower amount of time spent, like +the Reality Upgrade "Replicative Rapidity" for example.

You can buy upgrades for the Black Hole by using Reality Machines. There are three upgrades for the Black Hole: @@ -954,15 +1048,8 @@ Duration - How long each speed burst lasts before going back to normal speed, increased by ${formatPercents(0.3)} per upgrade.

-Once you reach ${formatInt(1)} year of game playtime, you unlock a Reality upgrade that allows you to have -a second Black Hole. The time spent for this requirement is itself affected by the first Black Hole, so it -takes much less than ${formatInt(1)} actual real-time year. -
-
-Once the Black Hole is active at least ${formatPercents(0.9999, 2)} of the time, it becomes permanently active. -This is tracked separately for the two Black Holes. -
-
+Once you have ${formatInt(1)} year of game time on your save, you unlock a Reality Upgrade that allows +you to have a second Black Hole. The timer on the second Black Hole only advances when the first Black Hole is active. So, for example, if the first Black Hole has a duration of ${formatInt(4)} minutes and the second has an interval of ${formatInt(8)} minutes, the second Black Hole will only activate once every two cycles of the first Black Hole regardless of how short the @@ -971,9 +1058,25 @@ the actual time until the second Black Hole activates; in the Black Hole tab, yo the first Black Hole active needed for the second Black Hole to activate.

+When a Black Hole is active at least ${formatPercents(0.9999, 2)} of the time, it becomes permanently active. +This is tracked separately for the two Black Holes. +
+
+While offline, Black Hole cycles will still advance normally and their active speed boosts will apply fully as if the +game were still open. Offline time simulates segments of inactive and active Black Holes with different tick lengths +in order to reduce the negative effects of small tick count during active periods; the entry for "Offline Progress" +has been updated with more technical details. +
+
The Black Holes can be paused, completely halting their interval/duration cycle. However, when unpausing them, it will -take ${BlackHoles.ACCELERATION_TIME} real-time seconds for them to reach maximum speed if they were paused while their -speed boost was active. Pausing and unpausing affects both Black Holes; they can't be paused or unpaused independently. +take ${BlackHoles.ACCELERATION_TIME} real-time seconds for them to go from inactive to their maximum boosted speed. +This acceleration time will still advance the cycle as if it were running at full speed; so +while pausing gives some more control, it also ultimately results in some boosted time being lost. +
+
+Pausing and unpausing affects both Black Holes; they can't be paused or unpaused independently. They can be paused +automatically ${BlackHoles.ACCELERATION_TIME} real-time seconds before activation by toggling the relevant setting on +the Black Hole tab.

Hotkey: B will pause/unpause the Black Holes. @@ -984,9 +1087,9 @@ speed boost was active. Pausing and unpausing affects both Black Holes; they can }, { name: "Celestials", info: () => ` -Once you get all of the Reality upgrades, the first Celestial is unlocked. This opens up a new tab to the right of -Reality. The first subtab under the Celestials tab shows a map called "Celestial Navigation" which updates as you -progress through the game. Only part of the map will be visible when first unlocked, but new content will gradually +Once you get all of the Reality Upgrades, the first Celestial is unlocked. This opens up a new tab for Celestials, next +to the Reality tab. The first subtab under the Celestials tab shows a map called "Celestial Navigation" which updates as +you progress through the game. Only part of the map will be visible when first unlocked, but new content will gradually be revealed as you approach it, generally with a visual indication of your progress towards the next step.

@@ -1004,12 +1107,12 @@ the game will depend on the Celestial. name: "Teresa, Celestial of Reality", alias: "Teresa", info: () => ` -Teresa is the first Celestial. They are unlocked by Achievement 147, which requires obtaining all Reality upgrades. +Teresa is the first Celestial. They are unlocked by Achievement 147, which requires obtaining all Reality Upgrades.

On the main screen, there is a bar with a button above it that says "Pour RM". This allows you to put your RM into the container for a Reality Machine multiplier. RM which has been poured into the container can't be retrieved. -When you reach ${format(TERESA_UNLOCKS.RUN.price)} RM inside of the container, you unlock Teresa's Reality. +When you reach ${format(TeresaUnlocks.run.price)} RM inside of the container, you unlock Teresa's Reality.

When you complete Teresa's Reality, @@ -1017,7 +1120,7 @@ ${Teresa.runCompleted ? "your Glyph Sacrifice is multiplied based on the amount of antimatter gained during the run" : "
(complete Teresa's Reality to see the reward)
"}. Completing Teresa's Reality is only part of the story; you need to keep pouring RM in order to progress. Once -you are at ${format(TERESA_UNLOCKS.EFFARIG.price)} RM in the container, you'll unlock the next Celestial. +you are at ${format(TeresaUnlocks.effarig.price)} RM in the container, you'll unlock the next Celestial.

${Teresa.runCompleted @@ -1033,7 +1136,7 @@ ${Teresa.runCompleted alias: "Effarig", info: () => ` Effarig is the second Celestial you encounter. -They are unlocked by pouring at least ${format(TERESA_UNLOCKS.EFFARIG.price)} RM into Teresa's container. +They are unlocked by pouring at least ${format(TeresaUnlocks.effarig.price)} RM into Teresa's container.

Effarig introduces a currency called Relic Shards, which are obtained by using different kinds of Glyph effects during @@ -1057,15 +1160,19 @@ ${EffarigUnlock.run.isUnlocked
Completing Effarig's Reality unlocks ${EffarigUnlock.reality.isUnlocked - // Can't really make a nested template here without generally making a mess of the code - // eslint-disable-next-line prefer-template - ? "a new Glyph type, Effarig Glyphs. Effarig Glyphs have " + - formatInt(7) + " different possible effects, which you can view in the Glyph filter settings. You can only" + - " have one Effarig Glyph equipped at a time, and they can still only have at most " + formatInt(4) + - " effects. Lastly, the RM multiplier and Glyph instability effects can't appear together on the same Glyph." + ? `a new Glyph type, Effarig Glyphs. Effarig Glyphs have + ${formatInt(7)} different possible effects, which you can view in the Glyph filter settings. You can only + have one Effarig Glyph equipped at a time. +${Ra.unlocks.glyphEffectCount.canBeApplied + ? `Due to having Effarig at level 10 within Ra, there are no longer any restrictions on effects that appear on + Effarig Glyphs. Any given Effarig Glyph can now have up to all ${formatInt(7)} effects at the same time.` + : `Effarig Glyphs can only have at most ${formatInt(4)} effects, and the RM multiplier and Glyph instability + effects can't appear together on the same Glyph.`}` : "(complete Effarig's Reality to see reward details)"} +
+
`, - isUnlocked: () => Teresa.has(TERESA_UNLOCKS.EFFARIG), + isUnlocked: () => TeresaUnlocks.effarig.canBeApplied, tags: ["glyph", "sacrifice", "shards", "reality", "spectralflame", "lategame", "endgame", "celestial"], tab: "celestials/effarig" }, { @@ -1081,66 +1188,70 @@ Relic Shards. This system uses one of many methods to assign a score to your Gly with the highest score. After picking this Glyph, it checks the score against a threshold and either keeps it if the score is above the threshold, or sacrifices it instead. There are three basic modes:
-Lowest total sacrifice - Glyphs are given a score based on how much sacrifice value you have of that +Lowest total sacrifice: Glyphs are given a score based on how much sacrifice value you have of that particular Glyph's type. Glyphs of the type you have the least sacrifice value in will have the highest score. -This mode doesn't have a threshold and always sacrifices your glyphs. +This mode doesn't have a threshold and always sacrifices your Glyphs.
-Number of effects - Glyphs are given a score equal to the number of effects they have, and when multiple -glyphs have the same effect count, glyphs with higher rarity will be picked. The threshold they are +Number of effects: Glyphs are given a score equal to the number of effects they have, and when multiple +Glyphs have the same effect count, Glyphs with higher rarity will be picked. The threshold they are compared to is specified by your input in the text box.
-Rarity Threshold Mode - Glyphs are given a score equal to their rarity percent. The comparison threshold +Rarity Threshold Mode: Glyphs are given a score equal to their rarity percent. The comparison threshold can be set individually per Glyph type.

-Additionally, there are two more advanced modes with some additional flexibility: +Additionally, there are two more advanced modes with some additional flexibility. You may not need these initially, but +they can come in handy later on:
-Specified Effect Mode - Glyphs are given a score equal to their rarity and checked against the rarity threshold +Specified Effect Mode: Glyphs are given a score equal to their rarity and checked against the rarity threshold you specify, but this score is modified based on your inputs for effects. The Glyph will be checked for having a minimum number of effects and having all of the effects you choose, and its score is lowered by ${formatInt(200)} for every -missing effect. This guarantees that any Glyph that doesn't have the effects you want will be below the threshold. +missing effect. This guarantees that any Glyph that doesn't have the effects you want will be below the threshold. You +can forbid specific Glyph types by setting impossible conditions (eg. at least ${formatInt(6)} effects on a Power +Glyph will prevent Power Glyphs from being selected).
-Effect Score Mode - This mode is like Specified Effect Mode, but you have even finer control over the effects of -your Glyphs. The score of a Glyph is calculated from its rarity plus the score of each effect it has, and you can set -the threshold to any value you want. One possible way you can use this behavior is to give a weaker effect a value of -${formatInt(5)}, which allows you to keep Glyphs without that effect as long as they are rarer. +Effect Score Mode: The score of a Glyph is calculated from its rarity plus the score of each effect it has, +and you can set the threshold and values of each effect individually. Some possible ways this could be used: +
+- Giving a weaker effect a value of ${formatInt(5)} allows you to keep Glyphs without that effect as long as they are +rarer to compensate for being weaker +
+- Assigning a large negative score to a certain effect you do not want will forbid Glyphs with that effect from +being selected; this can be useful for effect testing and other more limited situations +
+- Setting an impossible condition (eg. a threshold score of ${formatInt(999)} and all effects worth ${formatInt(0)}) +will let you forbid entire types like Specified Effect Mode as well

-Glyph Set Saves are purchasable for ${format(GameDatabase.celestials.effarig.unlocks.setSaves.cost)} Relic +Glyph Presets are purchasable for ${format(GameDatabase.celestials.effarig.unlocks.setSaves.cost)} Relic Shards. This unlocks ${formatInt(5)} slots that allow you to save your currently equipped Glyphs into sets. You can't overwrite a set, you must delete it first. When you load a set, each Glyph in it is found and equipped. -If any are not found, it will display a warning, but equip all the rest regardless. You can only load a set when -there are no equipped Glyphs. When loading a set, you can be Level and/or Rarity sensitive. The best Glyph from -the possible Glyphs will always be the one equipped. Just like other groups of circular Glyphs, you can click -any of them in order to bring up a modal summarizing the whole set of Glyphs. -
-
-Note: If desired, "Specified Effect Mode" and "Effect Score Mode" can be used to filter out some Glyph types -entirely; for example setting impossible conditions like "at least ${formatInt(6)} effects" or "Minimum score -${formatInt(999)} and all effects worth ${formatInt(0)}" on Power Glyphs will make it so that a Power Glyph is -never picked. +If any are not found, it will display a warning, but equip all the rest regardless. +When loading a set, you can be Level and/or Rarity sensitive. The best Glyph from the possible Glyphs +will always be the one equipped. Just like other groups of circular Glyphs, you can click any of them +in order to bring up a modal summarizing the whole set of Glyphs. `, isUnlocked: () => EffarigUnlock.adjuster.isUnlocked, tags: ["glyph", "weight", "adjustment", "sacrifice", "filter", "threshold", "set", "save", "reality", "lategame", "endgame"], tab: "celestials/glyphfilter" }, { - name: "The Enslaved Ones, Celestial of Time", - alias: "Enslaved Ones", + name: "The Nameless Ones, Celestial of Time", + alias: "Nameless Ones", info: () => ` -The Enslaved Ones are the third Celestial, unlocked by completing Effarig's Eternity. +The Nameless Ones are the third Celestial, unlocked by completing Effarig's Eternity.

-When unlocking The Enslaved Ones, you immediately gain access to two new mechanics related to time. You can store +When unlocking The Nameless Ones, you immediately gain access to two new mechanics related to time. You can store "game time" by charging your Black Hole, and you can store "real time" by intentionally halting your production. -Stored game time is also used as a currency for purchasing unlocks from The Enslaved Ones. +Stored game time is also used as a currency for purchasing unlocks from The Nameless Ones.

-Charging your Black Hole gives you stored time, which it does at the expense of setting your game speed to -${formatInt(1)}. The game is in effect using your increased game speed in order to store time itself. Its -main use is to discharge the Black Hole, which takes uses your stored time to skip forward in time by a duration -equal to the time stored. This is different than regular game speed multipliers in that discharging is not subject to -any modifiers to game speed when it is used, only when it is stored. +Charging your Black Hole gives you stored game time, which it does at the expense of setting your game speed to +${formatInt(1)}. The game is in effect using your increased game speed in order to store game time itself. Its +main use is to discharge the Black Hole, which takes uses your stored game time to skip forward in time by a duration +equal to the game time stored. This is different than regular game speed multipliers in that discharging is not subject +to any modifiers to game speed when it is used, only when it is stored.

Storing real time completely stops all production, effectively pausing your game. For every real-time second that @@ -1154,33 +1265,38 @@ ${format(5e12)} Relic Shards, ${formatInt(5)} Glyphs (subject to your filtering and ${formatInt(5)} Perk Points.

+However, if your Reality has lasted for less than ${formatInt(1)} second, the amplification factor is capped by the +amount of seconds stored. For example, if you have ${formatInt(1000)} seconds stored and amplify a Reality which has +lasted ${format(0.2, 2, 2)} seconds, you will use ${formatInt(200)} seconds to simulate ${formatInt(1000)} Realities. +
+
You can toggle a setting to automatically store offline time as stored real time.

Their first unlock costs ${format(TimeSpan.fromMilliseconds(ENSLAVED_UNLOCKS.FREE_TICKSPEED_SOFTCAP.price).totalYears)} -years of stored time. It increases the softcap to Tickspeed Upgrades gained from Time Dimensions +years of stored game time. It increases the softcap to Tickspeed Upgrades gained from Time Dimensions (the point at which their cost starts increasing faster) by ${format(1e5)} Tickspeed Upgrades.

-At ${format(TimeSpan.fromMilliseconds(ENSLAVED_UNLOCKS.RUN.price).totalYears)} years of stored time, you are able to -finally unlock their Reality. The reward for completing The Enslaved Ones' Reality is +At ${format(TimeSpan.fromMilliseconds(ENSLAVED_UNLOCKS.RUN.price).totalYears)} years of stored game time, you are able +to finally unlock their Reality. The reward for completing The Nameless Ones' Reality is ${Enslaved.isCompleted ? "unlocking Tesseracts, which have their own How To Play entry." - : "(complete The Enslaved Ones' Reality to see reward details)"} + : "(complete The Nameless Ones' Reality to see reward details)"}

-The Enslaved Ones won't directly unlock the next Celestial. +The Nameless Ones won't directly unlock the next Celestial. `, isUnlocked: () => EffarigUnlock.eternity.isUnlocked, // TODO Add the rest of the testers here too before release; this is all only pre wave 1 tags: ["reality", "time", "blackhole", "lategame", "endgame", "testers", "celestial", "ikerstream", "realrapidjazz", "saturnus", "earth", "garnet", "pichusuperlover"], - tab: "celestials/enslaved" + tab: "celestials/nameless" }, { name: "Tesseracts", info: () => ` -Tesseracts are a new resource you unlock for completing The Enslaved Ones' Reality. +Tesseracts are a new resource you unlock for completing The Nameless Ones' Reality.

Infinity Dimensions are normally capped at ${format(InfinityDimensions.HARDCAP_PURCHASES)} total purchases, @@ -1190,7 +1306,7 @@ Tesseracts allow you to raise this cap by spending Infinity Points.
The cost of Tesseracts increases super-exponentially, but each successive Tesseract is significantly stronger than the last in order to make up for that. Tesseract count is never reset, meaning that once purchased, you don't need -to reach the IP cost again in order to take advantage of the raised cap in later realities. +to reach the IP cost again in order to take advantage of the raised cap in later Realities.

You can see additional information about your current Tesseract count and the cost of the next one in the Infinity @@ -1219,34 +1335,38 @@ ${format(GameDatabase.celestials.v.mainUnlock.dilatedTime.requirement)} Dilated ${format(GameDatabase.celestials.v.mainUnlock.replicanti.requirement)} Replicanti, all in the same Reality.

-When you meet all of those requirements, you'll be able to access V's Reality. However, completing the -Reality itself is only the beginning. V has six different requirements, each of which require you to make a -certain amount of progress within V's Reality. Completing a requirement rewards you with a V-Achievement. -V-Achievements are permanent and persist after exiting V's reality, and don't all need to be done simultaneously. -
-
-After completing the requirement, the V-Achievement threshold then increases and can be completed again -if you can reach the new goal. You can complete each category of V-Achievement up to six times. -Completed V-Achievements do two things: -
-- Upon reaching certain totals of V-Achievements, you automatically unlock upgrades on the V tab without needing -to spend any resources. -
-- Each V-Achievement also gives you one Space Theorem. -
-
-The goal reduction unlocked by having ${formatInt(2)} V-Achievements allows you to make some V-Achievement requirements -easier to complete by spending Perk Points, down to a limit of whatever the easiest tier requires. -The cost of reducing a goal does not increase as it is used, and will also reduce future tiers as well. -
-
-Space Theorems allow you to purchase Time Studies which are normally forbidden, such as multiple paths in the -split after the improved IP formula, or both Time Studies within a dark/light pair near the bottom. Like Time -Theorems, they are freely given back every time you respec your studies. -With enough Space Theorems you'll eventually be able to purchase every single Time Study at once! -
-
-Reaching ${formatInt(36)} V-Achievements (and therefore completing all of V's Achievements) unlocks the next Celestial. +When you meet all of those requirements, you'll be able to access V's Reality. +${VUnlocks.vAchievementUnlock.isUnlocked + ? `However, completing the Reality itself is only the beginning. V has six different requirements, each of which + require you to make a certain amount of progress within V's Reality. Completing a requirement rewards you with a + V-Achievement. + V-Achievements are permanent and persist after exiting V's Reality, and don't all need to be done simultaneously. +
+
+ After completing the requirement, the V-Achievement threshold then increases and can be completed again + if you can reach the new goal. You can complete each category of V-Achievement up to six times. + Completed V-Achievements do two things: +
+ - Upon reaching certain totals of V-Achievements, you automatically unlock upgrades on the V tab without needing + to spend any resources. +
+ - Each V-Achievement also gives you one Space Theorem. +
+
+ The goal reduction unlocked by having ${formatInt(2)} V-Achievements allows you to make some V-Achievement + requirements easier to complete by spending Perk Points, down to a limit of whatever the easiest tier requires. + The cost of reducing a goal does not increase as it is used, and will also reduce future tiers as well. +
+
+ Space Theorems allow you to purchase Time Studies which are normally forbidden, such as multiple paths in the + Pace Split after the improved IP formula, or both Time Studies within a dark/light pair near the bottom. + Like Time Theorems, they are freely given back every time you respec your studies. + With enough Space Theorems you'll eventually be able to purchase every single Time Study at once! +
+
+ Reaching ${formatInt(36)} V-Achievements (and therefore completing all of V's Achievements) unlocks the next + Celestial.` + : "(unlock V's Reality to see further details)"} `, isUnlocked: () => Achievement(151).isUnlocked, tags: ["reality", "lategame", "endgame", "girlfriend", "challenges", "achievement", "space", "theorems", @@ -1265,7 +1385,7 @@ Each previous Celestial within Ra gains levels by using memories, which are gene Memory Chunks. Memory Chunks can only be gained by entering Ra's Reality, but inside of the Reality Chunks will be generated passively based on certain resource totals. If you are storing real time, you will not gain any Chunks inside of Ra's Reality, but Memories will still be generated normally. Having a total of -${formatInt(RA_UNLOCKS.RA_RECOLLECTION_UNLOCK.totalLevels)} levels across all Celestials unlocks Recollection, +${formatInt(Ra.remembrance.requiredLevels)} levels across all Celestials unlocks Remembrance, which allows you to choose a particular Celestial to gain more chunks while inside of Ra's Reality.

@@ -1279,56 +1399,58 @@ improve your Glyph effects once you reach certain thresholds in Glyph sacrifice

At level ${formatInt(2)}, Effarig unlocks -${Ra.has(RA_UNLOCKS.EFFARIG_UNLOCK) +${Ra.unlocks.effarigUnlock.canBeApplied ? "a new mechanic called Glyph Alchemy and later on also makes Effarig Glyphs stronger while gradually removing " + "almost all random elements of Glyph generation. Glyph Alchemy also has its own How To Play entry." : "(unlock Effarig within Ra to see unlock details)"}

-The Enslaved Ones unlocks -${Ra.has(RA_UNLOCKS.ENSLAVED_UNLOCK) +The Nameless Ones unlocks +${Ra.unlocks.enslavedUnlock.canBeApplied ? "additional mechanics related to charging the Black Holes, as well as making them significantly stronger." - : "(unlock The Enslaved Ones within Ra to see unlock details)"} + : "(unlock The Nameless Ones within Ra to see unlock details)"}

V unlocks -${Ra.has(RA_UNLOCKS.V_UNLOCK) - ? "Triad Studies, which are new Studies near the bottom of the tree which cost Space Theorems. Each Triad Study " + +${Ra.unlocks.vUnlock.canBeApplied + ? "Triad Studies, which are new studies near the bottom of the tree which cost Space Theorems. Each Triad Study " + "requires you to also have the three nearby studies as well in order to purchase them. They also unlock a " + - "smaller set of more difficult V Achievements to complete for additional Space Theorems." + "smaller set of more difficult V-Achievements to complete for additional Space Theorems." : "(unlock V within Ra to see unlock details)"}

Ra won't directly unlock the next Celestial.`, - isUnlocked: () => V.has(V_UNLOCKS.RA_UNLOCK), + isUnlocked: () => VUnlocks.raUnlock.isUnlocked, tags: ["reality", "memories", "razenpok", "levels", "glyphs", "lategame", "endgame", - "effarig", "teresa", "enslaved", "v", "celestial"], + "effarig", "teresa", "nameless", "v", "celestial"], tab: "celestials/ra" }, { name: "Glyph Alchemy", info: () => ` Glyph Alchemy is a mechanic unlocked by reaching Effarig level ${formatInt(2)} in Ra. It unlocks the ability to -use up your glyphs by refining them into alchemy resources associated with their type. Each resource gives some -kind of a boost to certain parts of the game based on how much of them you have. +use up your Glyphs by refining them into Alchemy Resources associated with their type. You can refine Glyphs by +setting your Sacrifice Type to something other than "Always Sacrifice" in the Glyphs tab, and doing the normal +procedure for a sacrifice. +Each Alchemy Resource has a unique effect, which you can view on the Alchemy tab.

In addition to all their other properties, Glyphs now have a refinement value which determines how much of -its associated alchemy resource it is worth. This value is based on the cube of the Glyph's level, scaled -so that level ${formatInt(10000)} glyphs correspond to ${formatInt(10000)} alchemy resources. A single Glyph itself, +its associated Alchemy Resource it is worth. This value is based on the cube of the Glyph's level, scaled +so that level ${formatInt(10000)} Glyphs correspond to ${formatInt(10000)} Alchemy Resources. A single Glyph itself, however, only gives ${formatPercents(GlyphSacrificeHandler.glyphRefinementEfficiency)} of this value when refined. These are values for ${formatPercents(1)} rarity Glyphs; Glyphs of lower rarity still have the same cap but give proportionally less resources. For example, a ${formatPercents(0.5)} rarity Glyph will give only half as much.

-Alchemy resources can't be gained indefinitely; there is a per-resource cap which is based on the highest refinement +Alchemy Resources can't be gained indefinitely; there is a per-resource cap which is based on the highest refinement value of all the Glyphs of that type you have refined. For example, if the highest level Time Glyph you have refined -is level ${formatInt(8000)} (alchemy value: ${formatInt(GlyphSacrificeHandler.levelRefinementValue(8000))}), then no +is level ${formatInt(8000)} (refinement value: ${formatInt(GlyphSacrificeHandler.levelRefinementValue(8000))}), then no matter how many Time Glyphs you refine, you can never have more than ${formatInt(GlyphSacrificeHandler.levelRefinementValue(8000))} of the Time resource until you refine another Time Glyph with a higher refinement value.

-Alchemy resources can be combined together in certain combinations in order to create new compound resources, which +Alchemy Resources can be combined together in certain combinations in order to create new compound resources, which are unlocked at certain Effarig levels. Resources are combined once per Reality, unaffected by real time amplification. Reactions have a higher yield and thus happen faster when your reagent amounts are higher. The cap for compound resources is equal to the lowest cap amongst all of its reagents. @@ -1338,7 +1460,7 @@ To activate or deactivate a reaction, click the circle corresponding to the reac be applied, moving lines will be shown from all reagents to the product. If a connection is a solid line, that means that the reaction can't proceed due to not having enough of that reagent to get more of the product due to its cap. `, - isUnlocked: () => Ra.has(RA_UNLOCKS.GLYPH_ALCHEMY), + isUnlocked: () => Ra.unlocks.unlockGlyphAlchemy.canBeApplied, // Oh god I'm so sorry this is so many words tags: ["reality", "lategame", "endgame", "ra", "effarig", "alchemy", "power", "infinity", "time", "replication", "dilation", "cardinality", "eternity", "dimensionality", "inflation", "alternation", "synergism", "momentum", @@ -1378,7 +1500,7 @@ Imaginary Machine upgrades will unlock the final two Celestials. name: "Lai'tela, Celestial of Dimensions", alias: "Lai'tela", info: () => ` -Lai'tela is the sixth Celestial, unlocked by purchasing the appropriate Imaginary upgrade for +Lai'tela is the sixth Celestial, unlocked by purchasing the appropriate Imaginary Upgrade for ${format(ImaginaryUpgrade(15).cost)} iM.

@@ -1394,7 +1516,7 @@ Each Dark Matter Dimension, after a certain interval of time, generates two thin Dark Matter Dimension and another resource called Dark Energy. Dark Matter and Dark Matter Dimension production per interval is equal to the product of your Dark Matter multiplier and the number of dimensions you have, while Dark Energy production is independent of your dimension amount. Dark Energy is used to produce Singularities, which -have their own How to Play entry. +have their own How To Play entry.

Dark Matter Dimensions can have their intervals upgraded down to a minimum of ${formatInt(10)}ms, at which point @@ -1407,7 +1529,7 @@ again. Reaching ${formatInt(10)}ms again allows you to ascend again if you choos An Imaginary Upgrade allows you to unlock a prestige called Annihilation. Annihilation resets your Dark Matter and Dark Matter Dimensions, but adds to a permanent multiplier to Dark Matter that applies to all Dark Matter Dimensions. You can Annihilate multiple times; the additions to the multiplier stack additively, and there is -no need to annihilate for a greater addition each time. You must have at least +no need to Annihilate for a greater addition each time. You must have at least ${format(Laitela.annihilationDMRequirement)} Dark Matter in order to Annihilate.

@@ -1434,7 +1556,7 @@ an appropriate portion of their multiplier.
The purchase buttons for Antimatter Dimensions and Tickspeed Upgrades become modified to display the number of upgrades you would be able to purchase if Continuum was inactive, and the purchase count is scaled smoothly with antimatter. -For example, having ${format(2e7)} antimatter will give you a Continuum value of ${format(5.3, 0, 1)} for Tickspeed +For example, having ${format(2e7)} antimatter will give you a Continuum value of ${format(5.3, 0, 1)} for tickspeed (initial cost of ${format(1e3)} and increase of ${formatX(10)}) since you can purchase it ${formatInt(5)} times and are roughly ${formatPercents(0.3)} of the way to the next. Tickspeed Continuum in this case will then give a production boost equal to (upgrade multiplier)${format(5.3, 0, 1)}. @@ -1488,51 +1610,62 @@ Independently of the milestone type, milestones also have an icon indicating wha alias: "Pelle", info: () => ` When you purchase the last Imaginary Upgrade and unlock Pelle, you unlock their tab, where you can find a button to -"Doom your Reality". Dooming your Reality will start a new Doomed Reality, resetting almost the entire game up to -Reality, not giving you any rewards from your progress in your current Reality. -
-When you enter the Doomed Reality, you'll keep all values under the General- and Reality header in the Statistics -tab and all of your best Challenge times. Inside Doomed Realities, multiple upgrades, Time Studies, Challenge and -Celestial rewards, Perks, and other game mechanics are disabled or grant no reward. -You can view the "Show effects in Doomed Reality" in Pelle tab for further information. +"Doom your Reality". In order to Doom your Reality, you must have completed all ${formatInt(17)} rows of Achievements +available to you at this point, and attained ${formatInt(25000)} of each Alchemy Resource.

-Remnants are a new currency gained on Armageddon resets. Remnant gain is based on your best ever Antimatter, Infinity- -and Eternity Points across all Doomed Realities. Remnants produce Reality Shards which can be spent on Pelle Upgrades. -
-
-Pelle Upgrades can be divided into two categories. The five upgrades in the first row can be repeatedly bought, but -eventually reach a cap. They grant boosts to different aspects of the game, making progression within Doomed Realities -easier. -
-The other upgrades in the bottom rows offer automation and QoL (quality of life) improvements. Everything unlocked from -these upgrades, can't be unlocked by anything else in the game. So for example completing a Normal Challenge won't -give you the corresponding Antimatter Dimension autobuyer back as these are locked behind Pelle Upgrades. -You can toggle a button above upgrade to hide bought upgrades or click the --icon to collapse and hide the entire panel. -
-
-Hotkey: Z will try to perform an Armageddon reset. +${Pelle.isDoomed + ? `Dooming your Reality will start a new Doomed Reality, resetting almost the entire game up to + Reality, not giving you any rewards from your progress in your current Reality. +
+
+ When you enter the Doomed Reality, you keep all values under the General and Reality headers in the Statistics + tab and all of your best Challenge times. Inside Doomed Realities, multiple upgrades, Time Studies, Challenge and + Celestial rewards, Perks, and other game mechanics are disabled or grant no reward. + You can view the "Show effects in Doomed Reality" in Pelle tab for further information. +
+
+ Remnants are a new currency gained on Armageddon resets. Remnant gain is based on your best ever antimatter, + Infinity Points, and Eternity Points across all Doomed Realities. Remnants produce Reality Shards which can be + spent on Pelle Upgrades. +
+
+ Pelle Upgrades can be divided into two categories. The five upgrades in the first row can be repeatedly bought, + but eventually reach a cap. They grant boosts to different aspects of the game, making progression within Doomed + Realities easier. +
+
+ The other upgrades in the bottom rows offer automation and QoL (quality of life) improvements. Everything unlocked + from these upgrades can't be unlocked by anything else in the game; for example, completing a Normal Challenge + won't give you the corresponding Antimatter Dimension autobuyer back as these are locked behind Pelle Upgrades. + You can toggle a button above upgrade to hide bought upgrades or click the + -icon to collapse and hide the entire panel. +
+
+ Hotkey: Z will try to perform an Armageddon reset.` + : "You must Doom your Reality to read the rest of this entry." +} `, - isUnlocked: () => Pelle.isDoomed, - tags: ["reality", "antimatter", "lategame", "endgame", "final", "hevipelle", "celestial"], + isUnlocked: () => Pelle.isUnlocked, + tags: ["reality", "antimatter", "lategame", "endgame", "final", "hevipelle", "celestial", "doom"], tab: "celestials/pelle" }, { name: "Pelle Strikes", info: () => ` Pelle Strikes are encountered on different events in the Doomed Reality. You have encountered the first Pelle Strike by -reaching Infinity the first time within a Doomed Reality. More Strikes eventually occur by further progression. +reaching Infinity for the first time within a Doomed Reality. More Strikes eventually occur by further progression. Each Pelle Strike adds a nerf to a specific aspect of the game, which can be seen by clicking on the Strike name. Each Pelle Strike also unlocks a Rift bar.
-Rift bars can be filled by clicking them to toggle between "Idle" and "Filling". When active, Rifts consume -${formatInt(3)}% of a Rift specific resource per second. Each Rift offers a Rift specific effect which are based -on the total amount filled. +
+Rift bars can be filled by clicking them to toggle between "Idle" and "Filling", although only two Rifts can be +"Filling" at any given time. When active, Rifts consume ${formatInt(3)}% of a Rift-specific resource per second. Each +Rift offers a Rift-specific effect which are based on the total amount filled. ${PelleStrikes.eternity.hasStrike - ? `An exception for this is Pestilence, which effect gets capped once you have drained a total of - ${formatPostBreak(DC.E2000)} replicanti.` + ? `An exception for this is Decay/Collapse/Disarray, whose effect gets capped once you have drained a total of + ${formatPostBreak(DC.E2000)} Replicanti.` : ""} -In addition each Rift offers three rewards for filling them up to a certain percentage. +In addition, each Rift offers three milestone rewards for filling them up to a certain percentage. `, isUnlocked: () => PelleStrikes.infinity.hasStrike, tags: ["reality", "antimatter", "lategame", "endgame", "final", "pelle", "strike", "rift", "celestial"], @@ -1540,12 +1673,16 @@ In addition each Rift offers three rewards for filling them up to a certain perc }, { name: "The Galaxy Generator", info: () => ` -When you reach ${formatInt(100)}% War, you unlock the Galaxy Generator, which can passively generate -Galaxies. Generated Galaxies are like Replicanti Galaxies and Tachyon Galaxies in that they affect Tickspeed as if -they were Antimatter Galaxies but they don't increase the cost of your next Antimatter Galaxy. You also unlock five -new upgrades. The first upgrade increases the base amount of Galaxies generated. The other four upgrades then give -a multiplier to this base amount. The first two upgrades can be bought by spending Antimatter- and Generated -Galaxies. Replicanti- or Tachyon Galaxies can't be spent for purchasing those upgrades.`, +When you reach ${formatInt(100)}% Recursion/Dispersion/Destruction, you unlock the Galaxy Generator, which can +passively generate Galaxies. Generated Galaxies are like Replicanti Galaxies and Tachyon Galaxies in that they affect +tickspeed as if they were Antimatter Galaxies, but they don't increase the cost of your next Antimatter Galaxy. You +also unlock five new upgrades. The first upgrade increases the base amount of Galaxies generated. The other four +upgrades then give a multiplier to this base amount. The first two upgrades can be bought by spending antimatter and +Generated Galaxies. Replicanti or Tachyon Galaxies can't be spent for purchasing those upgrades. +
+
+The Galaxy Generator has a maximum number of Galaxies it can generate, which can only be increased by draining +Rifts once the current cap has been reached.`, isUnlocked: () => Pelle.hasGalaxyGenerator, tags: ["reality", "antimatter", "lategame", "endgame", "final", "pelle", "galaxy", "galaxies", "generator", "celestial"], diff --git a/javascripts/core/secret-formula/index.js b/javascripts/core/secret-formula/index.js index 7dfdcd1be..cd5507951 100644 --- a/javascripts/core/secret-formula/index.js +++ b/javascripts/core/secret-formula/index.js @@ -1,42 +1,57 @@ -import "./game-database.js"; +/* eslint-disable import/newline-after-import */ +/* eslint-disable import/first */ +import "./game-database"; -import "./tabs.js"; -import "./away-progress-types.js"; -import "./confirmation-types.js"; -import "./tab-notifications.js"; -import "./news.js"; -import "./achievements/normal-achievements.js"; -import "./achievements/secret-achievements.js"; -import "./challenges/normal-challenges.js"; -import "./challenges/infinity-challenges.js"; -import "./challenges/eternity-challenges.js"; -import "./infinity/infinity-upgrades.js"; -import "./infinity/break-infinity-upgrades.js"; -import "./eternity/time-studies/normal-time-studies.js"; -import "./eternity/time-studies/ec-time-studies.js"; -import "./eternity/time-studies/dilation-time-studies.js"; -import "./eternity/eternity-upgrades.js"; -import "./eternity/eternity-milestones.js"; -import "./eternity/dilation-upgrades.js"; -import "./reality/reality-upgrades.js"; -import "./reality/imaginary-upgrades.js"; -export * from "./reality/perks.js"; -import "./reality/automator.js"; -import "./reality/glyph-sacrifices.js"; -import "./celestials/perk-shop.js"; -import "./celestials/effarig.js"; -import "./celestials/pelle-upgrades.js"; -import "./celestials/strikes.js"; -import "./celestials/rifts.js"; -import "./celestials/galaxy-generator.js"; -import "./celestials/ra.js"; -import "./celestials/pelle-quotes.js"; -import "./celestials/enslaved.js"; -export * from "./celestials/v.js"; -import "./celestials/alchemy.js"; -import "./shop-purchases.js"; -export * from "./celestials/singularity-milestones.js"; -import "./script-templates.js"; -import "./speedrun-milestones.js"; +import "./tabs"; +import "./away-progress-types"; +import "./catchup-resources"; +import "./progress-checker"; +import "./confirmation-types"; +import "./tab-notifications"; +import "./news"; +import "./achievements/normal-achievements"; +import "./achievements/secret-achievements"; +import "./challenges/normal-challenges"; +import "./challenges/infinity-challenges"; +import "./challenges/eternity-challenges"; +import "./infinity/infinity-upgrades"; +import "./infinity/break-infinity-upgrades"; +import "./eternity/time-studies/normal-time-studies"; +import "./eternity/time-studies/ec-time-studies"; +import "./eternity/time-studies/dilation-time-studies"; +import "./eternity/eternity-upgrades"; +import "./eternity/eternity-milestones"; +import "./eternity/dilation-upgrades"; +import "./reality/reality-upgrades"; +import "./reality/imaginary-upgrades"; +export * from "./reality/perks"; +import "./reality/automator"; +import "./reality/glyph-types"; +export * from "./reality/glyph-effects"; +import "./reality/glyph-sacrifices"; +import "./celestials/perk-shop"; +import "./celestials/teresa"; +import "./celestials/effarig"; +import "./celestials/pelle-upgrades"; +import "./celestials/strikes"; +import "./celestials/rifts"; +import "./celestials/galaxy-generator"; +import "./celestials/ra"; +import "./celestials/enslaved"; +import "./celestials/navigation"; +import "./celestials/navigation-sigils/galaxy-icon"; +import "./celestials/navigation-sigils/final-sigil"; +export * from "./celestials/v"; +import "./celestials/alchemy"; +import "./shop-purchases"; +export * from "./celestials/singularity-milestones"; +import "./script-templates"; +import "./speedrun-milestones"; -import "./h2p.js"; +import "./celestials/quotes/index"; + +import "./h2p"; + +import "./credits"; + +import "./changelog"; diff --git a/javascripts/core/secret-formula/infinity/break-infinity-upgrades.js b/javascripts/core/secret-formula/infinity/break-infinity-upgrades.js index dc6c590fe..b570463eb 100644 --- a/javascripts/core/secret-formula/infinity/break-infinity-upgrades.js +++ b/javascripts/core/secret-formula/infinity/break-infinity-upgrades.js @@ -1,145 +1,145 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; - -GameDatabase.infinity.breakUpgrades = (function() { - function rebuyable(config) { - const effectFunction = config.effect || (x => x); - return { - id: config.id, - cost: () => config.initialCost * Math.pow(config.costIncrease, player.infinityRebuyables[config.id]), - maxUpgrades: config.maxUpgrades, - description: config.description, - effect: () => effectFunction(player.infinityRebuyables[config.id]), - isDisabled: config.isDisabled, - // There isn't enough room in the button to fit the EC reduction and "Next:" at the same time while still - // presenting all the information in an understandable way, so we only show it if the upgrade is maxed - formatEffect: config.formatEffect || - (value => { - const afterECText = config.afterEC ? config.afterEC() : ""; - return value === config.maxUpgrades - ? `Default: ${formatX(10)} | Currently: ${formatX(10 - value)} ${afterECText}` - : `Default: ${formatX(10)} | Currently: ${formatX(10 - value)} Next: ${formatX(10 - value - 1)}`; - }), - formatCost: value => format(value, 2, 0), - noLabel: !config.label - }; - } +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; +function rebuyable(config) { + const effectFunction = config.effect || (x => x); + const { id, maxUpgrades, description, isDisabled, noLabel, onPurchased } = config; return { - totalAMMult: { - id: "totalMult", - cost: 1e4, - description: "Antimatter Dimensions gain a multiplier based on total antimatter produced", - effect: () => Math.pow(player.records.totalAntimatter.exponent + 1, 0.5), - formatEffect: value => formatX(value, 2, 2) - }, - currentAMMult: { - id: "currentMult", - cost: 5e4, - description: "Antimatter Dimensions gain a multiplier based on current antimatter", - effect: () => Math.pow(Currency.antimatter.exponent + 1, 0.5), - formatEffect: value => formatX(value, 2, 2) - }, - galaxyBoost: { - id: "postGalaxy", - cost: 5e11, - description: () => `All Galaxies are ${formatPercents(0.5)} stronger`, - effect: 1.5 - }, - infinitiedMult: { - id: "infinitiedMult", - cost: 1e5, - description: "Antimatter Dimensions gain a multiplier based on Infinities", - effect: () => 1 + Currency.infinitiesTotal.value.pLog10() * 10, - formatEffect: value => formatX(value, 2, 2) - }, - achievementMult: { - id: "achievementMult", - cost: 1e6, - description: "Additional multiplier to Antimatter Dimensions based on Achievements completed", - effect: () => Math.max(Math.pow((Achievements.effectiveCount - 30), 3) / 40, 1), - formatEffect: value => formatX(value, 2, 2) - }, - slowestChallengeMult: { - id: "challengeMult", - cost: 1e7, - description: "Antimatter Dimensions gain a multiplier based on slowest challenge run", - effect: () => Decimal.clampMin(50 / Time.worstChallenge.totalMinutes, 1), - formatEffect: value => formatX(value, 2, 2), - hasCap: true, - cap: DC.D3E4 - }, - infinitiedGen: { - id: "infinitiedGeneration", - cost: 2e7, - description: () => (Pelle.isDoomed - ? "This upgrade has no effect while in Doomed" - : "Passively generate Infinities based on your fastest Infinity"), - effect: () => player.records.bestInfinity.time, - formatEffect: value => { - if (Pelle.isDoomed) return "Disabled"; - if (value === Number.MAX_VALUE && !Pelle.isDoomed) return "No Infinity generation"; - let infinities = DC.D1; - infinities = infinities.timesEffectsOf( - RealityUpgrade(5), - RealityUpgrade(7) - ); - infinities = infinities.times(getAdjustedGlyphEffect("infinityinfmult")); - infinities = infinities.times(RA_UNLOCKS.TT_BOOST.effect.infinity()); - return `${quantify("Infinity", infinities)} every ${Time.bestInfinity.times(5).toStringShort()}`; - } - }, - autobuyMaxDimboosts: { - id: "autobuyMaxDimboosts", - cost: 5e9, - description: "Unlock the buy max Dimension Boost Autobuyer mode" - }, - autobuyerSpeed: { - id: "autoBuyerUpgrade", - cost: 1e15, - description: "Autobuyers unlocked or improved by Normal Challenges work twice as fast" - }, - tickspeedCostMult: rebuyable({ - id: 0, - initialCost: 1e6, - costIncrease: 5, - maxUpgrades: 8, - description: "Reduce post-infinity Tickspeed Upgrade cost multiplier scaling", - afterEC: () => (EternityChallenge(11).completions > 0 - ? `After EC11: ${formatX(Player.tickSpeedMultDecrease, 2, 2)}` - : "" - ), - label: false, - }), - dimCostMult: rebuyable({ - id: 1, - initialCost: 1e7, - costIncrease: 5e3, - maxUpgrades: 7, - description: "Reduce post-infinity Antimatter Dimension cost multiplier scaling", - afterEC: () => (EternityChallenge(6).completions > 0 - ? `After EC6: ${formatX(Player.dimensionMultDecrease, 2, 2)}` - : "" - ), - label: false, - }), - ipGen: rebuyable({ - id: 2, - initialCost: 1e7, - costIncrease: 10, - maxUpgrades: 10, - effect: value => Player.bestRunIPPM.times(value / 20), - description: () => { - let generation = `Generate ${formatInt(5 * player.infinityRebuyables[2])}%`; - if (!BreakInfinityUpgrade.ipGen.isCapped) { - generation += ` ➜ ${formatInt(5 * (1 + player.infinityRebuyables[2]))}%`; - } - const offlineString = player.options.offlineProgress ? ", works offline" : ""; - return `${generation} of your best IP/min from your last 10 Infinities${offlineString}`; - }, - isDisabled: effect => effect.eq(0), - formatEffect: value => `${format(value, 2, 1)} IP/min`, - label: true - }) + rebuyable: true, + id, + cost: () => config.initialCost * Math.pow(config.costIncrease, player.infinityRebuyables[config.id]), + maxUpgrades, + description, + effect: () => effectFunction(player.infinityRebuyables[config.id]), + isDisabled, + // There isn't enough room in the button to fit the EC reduction and "Next:" at the same time while still + // presenting all the information in an understandable way, so we only show it if the upgrade is maxed + formatEffect: config.formatEffect || + (value => { + const afterECText = config.afterEC ? config.afterEC() : ""; + return value === config.maxUpgrades + ? `Default: ${formatX(10)} | Currently: ${formatX(10 - value)} ${afterECText}` + : `Default: ${formatX(10)} | Currently: ${formatX(10 - value)} Next: ${formatX(10 - value - 1)}`; + }), + formatCost: value => format(value, 2, 0), + noLabel, + onPurchased }; -}()); +} + +GameDatabase.infinity.breakUpgrades = { + totalAMMult: { + id: "totalMult", + cost: 1e4, + description: "Antimatter Dimensions gain a multiplier based on total antimatter produced", + effect: () => Math.pow(player.records.totalAntimatter.exponent + 1, 0.5), + formatEffect: value => formatX(value, 2, 2) + }, + currentAMMult: { + id: "currentMult", + cost: 5e4, + description: "Antimatter Dimensions gain a multiplier based on current antimatter", + effect: () => Math.pow(Currency.antimatter.exponent + 1, 0.5), + formatEffect: value => formatX(value, 2, 2) + }, + galaxyBoost: { + id: "postGalaxy", + cost: 5e11, + description: () => `All Galaxies are ${formatPercents(0.5)} stronger`, + effect: 1.5 + }, + infinitiedMult: { + id: "infinitiedMult", + cost: 1e5, + description: "Antimatter Dimensions gain a multiplier based on Infinities", + effect: () => 1 + Currency.infinitiesTotal.value.pLog10() * 10, + formatEffect: value => formatX(value, 2, 2) + }, + achievementMult: { + id: "achievementMult", + cost: 1e6, + description: "Additional multiplier to Antimatter Dimensions based on Achievements completed", + effect: () => Math.max(Math.pow((Achievements.effectiveCount - 30), 3) / 40, 1), + formatEffect: value => formatX(value, 2, 2) + }, + slowestChallengeMult: { + id: "challengeMult", + cost: 1e7, + description: "Antimatter Dimensions gain a multiplier based on slowest challenge run", + effect: () => Decimal.clampMin(50 / Time.worstChallenge.totalMinutes, 1), + formatEffect: value => formatX(value, 2, 2), + hasCap: true, + cap: DC.D3E4 + }, + infinitiedGen: { + id: "infinitiedGeneration", + cost: 2e7, + description: "Passively generate Infinities based on your fastest Infinity", + effect: () => player.records.bestInfinity.time, + formatEffect: value => { + if (value === Number.MAX_VALUE && !Pelle.isDoomed) return "No Infinity generation"; + let infinities = DC.D1; + infinities = infinities.timesEffectsOf( + RealityUpgrade(5), + RealityUpgrade(7), + Ra.unlocks.continuousTTBoost.effects.infinity + ); + infinities = infinities.times(getAdjustedGlyphEffect("infinityinfmult")); + return `${quantify("Infinity", infinities)} every ${Time.bestInfinity.times(5).toStringShort()}`; + } + }, + autobuyMaxDimboosts: { + id: "autobuyMaxDimboosts", + cost: 5e9, + description: "Unlock the buy max Dimension Boost Autobuyer mode" + }, + autobuyerSpeed: { + id: "autoBuyerUpgrade", + cost: 1e15, + description: "Autobuyers unlocked or improved by Normal Challenges work twice as fast" + }, + tickspeedCostMult: rebuyable({ + id: 0, + initialCost: 1e6, + costIncrease: 5, + maxUpgrades: 8, + description: "Reduce post-infinity Tickspeed Upgrade cost multiplier scaling", + afterEC: () => (EternityChallenge(11).completions > 0 + ? `After EC11: ${formatX(Player.tickSpeedMultDecrease, 2, 2)}` + : "" + ), + noLabel: true, + onPurchased: () => GameCache.tickSpeedMultDecrease.invalidate() + }), + dimCostMult: rebuyable({ + id: 1, + initialCost: 1e7, + costIncrease: 5e3, + maxUpgrades: 7, + description: "Reduce post-infinity Antimatter Dimension cost multiplier scaling", + afterEC: () => (EternityChallenge(6).completions > 0 + ? `After EC6: ${formatX(Player.dimensionMultDecrease, 2, 2)}` + : "" + ), + noLabel: true, + onPurchased: () => GameCache.dimensionMultDecrease.invalidate() + }), + ipGen: rebuyable({ + id: 2, + initialCost: 1e7, + costIncrease: 10, + maxUpgrades: 10, + effect: value => Player.bestRunIPPM.times(value / 20), + description: () => { + let generation = `Generate ${formatInt(5 * player.infinityRebuyables[2])}%`; + if (!BreakInfinityUpgrade.ipGen.isCapped) { + generation += ` ➜ ${formatInt(5 * (1 + player.infinityRebuyables[2]))}%`; + } + const offlineString = player.options.offlineProgress ? ", works offline" : ""; + return `${generation} of your best IP/min from your last 10 Infinities${offlineString}`; + }, + isDisabled: effect => effect.eq(0), + formatEffect: value => `${format(value, 2, 1)} IP/min`, + noLabel: false + }) +}; diff --git a/javascripts/core/secret-formula/infinity/infinity-upgrades.js b/javascripts/core/secret-formula/infinity/infinity-upgrades.js index 94880ff3a..237700425 100644 --- a/javascripts/core/secret-formula/infinity/infinity-upgrades.js +++ b/javascripts/core/secret-formula/infinity/infinity-upgrades.js @@ -1,226 +1,237 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; -GameDatabase.infinity.upgrades = (function() { - function dimInfinityMult() { - return Currency.infinitiesTotal.value.times(0.2).plus(1); - } - function chargedDimInfinityMult() { - return 1 + Math.log10(Math.max(1, Currency.infinitiesTotal.value.pLog10())) * Math.sqrt(Ra.pets.teresa.level) / 150; - } - return { - totalTimeMult: { - id: "timeMult", - cost: 1, - description: "Antimatter Dimensions gain a multiplier based on time played", - effect: () => Math.pow(Time.totalTimePlayed.totalMinutes / 2, 0.15), - formatEffect: value => formatX(value, 2, 2), - charged: { - description: "Antimatter Dimensions gain a power effect based on time played and Teresa level", - effect: () => 1 + - Math.log10(Math.log10(Time.totalTimePlayed.totalMilliseconds)) * - Math.pow(Ra.pets.teresa.level, 0.5) / 150, - formatEffect: value => formatPow(value, 4, 4) - } - }, - dim18mult: { - id: "18Mult", - cost: 1, - description: "1st and 8th Antimatter Dimensions gain a multiplier based on Infinities", - effect: () => dimInfinityMult(), - formatEffect: value => formatX(value, 1, 1), - charged: { - description: "1st and 8th Antimatter Dimensions gain a power effect based on Infinities and Teresa level", - effect: () => chargedDimInfinityMult(), - formatEffect: value => formatPow(value, 4, 4) - } - }, - dim27mult: { - id: "27Mult", - cost: 1, - description: "2nd and 7th Antimatter Dimensions gain a multiplier based on Infinities", - effect: () => dimInfinityMult(), - formatEffect: value => formatX(value, 1, 1), - charged: { - description: "2nd and 7th Antimatter Dimensions gain a power effect based on Infinities and Teresa level", - effect: () => chargedDimInfinityMult(), - formatEffect: value => formatPow(value, 4, 4) - } - }, - dim36mult: { - id: "36Mult", - cost: 1, - description: "3rd and 6th Antimatter Dimensions gain a multiplier based on Infinities", - effect: () => dimInfinityMult(), - formatEffect: value => formatX(value, 1, 1), - charged: { - description: "3rd and 6th Antimatter Dimensions gain a power effect based on Infinities and Teresa level", - effect: () => chargedDimInfinityMult(), - formatEffect: value => formatPow(value, 4, 4) - } - }, - dim45mult: { - id: "45Mult", - cost: 1, - description: "4th and 5th Antimatter Dimensions gain a multiplier based on Infinities", - effect: () => dimInfinityMult(), - formatEffect: value => formatX(value, 1, 1), - charged: { - description: "4th and 5th Antimatter Dimensions gain a power effect based on Infinities and Teresa level", - effect: () => chargedDimInfinityMult(), - formatEffect: value => formatPow(value, 4, 4) - } - }, - resetBoost: { - id: "resetBoost", - cost: 1, - description: () => - `Decrease the number of Dimensions needed for Dimension Boosts and Antimatter Galaxies by ${formatInt(9)}`, - effect: 9, - charged: { - description: () => "Decrease Dimension Boost requirement based on Teresa level", - effect: () => 1 / (1 + Math.sqrt(Ra.pets.teresa.level) / 10), - formatEffect: value => `${formatX(value, 4, 4)}` - } - }, - buy10Mult: { - id: "dimMult", - cost: 1, - description: () => `Increase the multiplier for buying ${formatInt(10)} Antimatter Dimensions`, - effect: () => 1.1, - formatEffect: () => `${formatX(2, 0, 1)} ➜ ${formatX(2.2, 0, 1)}`, - charged: { - description: () => `The multiplier for buying ${formatInt(10)} Antimatter Dimensions gains ` + - "a power effect based on Teresa level", - effect: () => 1 + Ra.pets.teresa.level / 200, - formatEffect: value => formatPow(value, 3, 3) - } - }, - galaxyBoost: { - id: "galaxyBoost", - cost: 2, - description: "All Galaxies are twice as strong", - effect: 2, - charged: { - description: "All Galaxies are stronger based on Teresa level", - effect: () => 2 + Math.sqrt(Ra.pets.teresa.level) / 100, - formatEffect: value => `+${formatPercents(value - 1)}` - } - }, - thisInfinityTimeMult: { - id: "timeMult2", - cost: 3, - description: "Antimatter Dimensions gain a multiplier based on time spent in current Infinity", - effect: () => Decimal.max(Math.pow(Time.thisInfinity.totalMinutes / 4, 0.25), 1), - formatEffect: value => formatX(value, 2, 2), - charged: { - description: - "Antimatter Dimensions gain a power effect based on time spent in current Infinity and Teresa level", - effect: () => 1 + - Math.log10(Math.log10(Time.thisInfinity.totalMilliseconds + 100)) * - Math.sqrt(Ra.pets.teresa.level) / 150, - formatEffect: value => formatPow(value, 4, 4) - } - }, - unspentIPMult: { - id: "unspentBonus", - cost: 5, - description: "Multiplier to 1st Antimatter Dimension based on unspent Infinity Points", - effect: () => Currency.infinityPoints.value.dividedBy(2).pow(1.5).plus(1), - formatEffect: value => formatX(value, 2, 2), - charged: { - description: "Multiplier to 1st Antimatter Dimension based on unspent Infinity Points, powered by Teresa level", - effect: () => Currency.infinityPoints.value.dividedBy(2).pow(Math.sqrt(Ra.pets.teresa.level) * 1.5).plus(1), - formatEffect: value => formatX(value, 2, 2) - } - }, - dimboostMult: { - id: "resetMult", - cost: 7, - description: "Increase Dimension Boost multiplier", - effect: () => 2.5, - formatEffect: () => `${formatX(2, 0, 1)} ➜ ${formatX(2.5, 0, 1)}`, - charged: { - description: "Dimension Boost multiplier gains a power effect based on Teresa level", - effect: () => 1 + Ra.pets.teresa.level / 200, - formatEffect: value => formatPow(value, 3, 3) - } - }, - ipGen: { - id: "passiveGen", - cost: 10, - description: () => (Pelle.isDoomed - ? "This upgrade has no effect while in Doomed" - : `Passively generate Infinity Points ${formatInt(10)} times slower than your fastest Infinity`), - // Cutting corners: this is not actual effect, but it is totalIPMult that is displyed on upgrade - effect: () => (Teresa.isRunning || V.isRunning || Pelle.isDoomed ? DC.D0 : GameCache.totalIPMult.value), - formatEffect: value => { - if (Teresa.isRunning || V.isRunning) return "Disabled in this reality"; - if (Pelle.isDoomed) return "Disabled"; - const income = format(value, 2, 0); - const period = player.records.bestInfinity.time >= 999999999999 - ? "∞" - : Time.bestInfinity.times(10).toStringShort(); - return `${income} every ${period}`; - }, - charged: { - description: () => - `Gain a percentage of your Reality Machines gained on Reality each real-time second, - percent increases with Teresa level`, - effect: () => Math.sqrt(Ra.pets.teresa.level) / 1000 * RA_UNLOCKS.TT_BOOST.effect.autoPrestige(), - formatEffect: value => `${formatPercents(value, 2)}` - } - }, - skipReset1: { - id: "skipReset1", - cost: 20, - description: () => - `Start every reset with ${formatInt(1)} Dimension Boost, automatically unlocking the 5th Antimatter Dimension`, - }, - skipReset2: { - id: "skipReset2", - cost: 40, - description: () => - `Start every reset with ${formatInt(2)} Dimension Boosts, automatically unlocking the 6th Antimatter Dimension`, - }, - skipReset3: { - id: "skipReset3", - cost: 80, - description: () => - `Start every reset with ${formatInt(3)} Dimension Boosts, automatically unlocking the 7th Antimatter Dimension`, - }, - skipResetGalaxy: { - id: "skipResetGalaxy", - cost: 300, - description: () => - `Start every reset with ${formatInt(4)} Dimension Boosts, automatically unlocking the 8th Antimatter Dimension; - and an Antimatter Galaxy`, - }, - ipOffline: { - id: "ipOffline", - cost: 1000, - description: () => (player.options.offlineProgress - ? `Only while offline, gain ${formatPercents(0.5)} of your best IP/min without using Max All` - : "This upgrade would give offline Infinity Point generation, but offline progress is currently disabled"), - effect: () => (player.options.offlineProgress - ? player.records.thisEternity.bestIPMsWithoutMaxAll.times(TimeSpan.fromMinutes(1).totalMilliseconds / 2) - : DC.D0), - isDisabled: () => !player.options.offlineProgress, - formatEffect: value => `${format(value, 2, 2)} IP/min`, - }, - ipMult: { - id: "ipMult", - cost: () => InfinityUpgrade.ipMult.cost, - costCap: DC.E6E6, - costIncreaseThreshold: DC.E3E6, - description: () => (Pelle.isDoomed - ? "This upgrade has no effect while in Doomed" - : `Multiply Infinity Points from all sources by ${formatX(2)}`), - // Normally the multiplier caps at e993k or so with 3300000 purchases, but if the cost is capped then we just give - // an extra e7k to make the multiplier look nice - effect: () => (player.IPMultPurchases >= 3300000 ? DC.E1E6 : DC.D2.pow(player.IPMultPurchases)), - cap: () => Effarig.eternityCap ?? DC.E1E6, - formatEffect: value => formatX(value, 2, 2), + +function dimInfinityMult() { + return Currency.infinitiesTotal.value.times(0.2).plus(1); +} +function chargedDimInfinityMult() { + return 1 + Math.log10(Math.max(1, Currency.infinitiesTotal.value.pLog10())) * Math.sqrt(Ra.pets.teresa.level) / 150; +} + +GameDatabase.infinity.upgrades = { + totalTimeMult: { + id: "timeMult", + cost: 1, + description: "Antimatter Dimensions gain a multiplier based on time played", + effect: () => Math.pow(Time.totalTimePlayed.totalMinutes / 2, 0.15), + formatEffect: value => formatX(value, 2, 2), + charged: { + description: "Antimatter Dimensions gain a power effect based on time played and Teresa level", + effect: () => 1 + + Math.log10(Math.log10(Time.totalTimePlayed.totalMilliseconds)) * + Math.pow(Ra.pets.teresa.level, 0.5) / 150, + formatEffect: value => formatPow(value, 4, 4) } - }; -}()); + }, + dim18mult: { + id: "18Mult", + cost: 1, + checkRequirement: () => InfinityUpgrade.totalTimeMult.isBought, + description: "1st and 8th Antimatter Dimensions gain a multiplier based on Infinities", + effect: () => dimInfinityMult(), + formatEffect: value => formatX(value, 1, 1), + charged: { + description: "1st and 8th Antimatter Dimensions gain a power effect based on Infinities and Teresa level", + effect: () => chargedDimInfinityMult(), + formatEffect: value => formatPow(value, 4, 4) + } + }, + dim27mult: { + id: "27Mult", + cost: 1, + checkRequirement: () => InfinityUpgrade.buy10Mult.isBought, + description: "2nd and 7th Antimatter Dimensions gain a multiplier based on Infinities", + effect: () => dimInfinityMult(), + formatEffect: value => formatX(value, 1, 1), + charged: { + description: "2nd and 7th Antimatter Dimensions gain a power effect based on Infinities and Teresa level", + effect: () => chargedDimInfinityMult(), + formatEffect: value => formatPow(value, 4, 4) + } + }, + dim36mult: { + id: "36Mult", + cost: 1, + checkRequirement: () => InfinityUpgrade.dim18mult.isBought, + description: "3rd and 6th Antimatter Dimensions gain a multiplier based on Infinities", + effect: () => dimInfinityMult(), + formatEffect: value => formatX(value, 1, 1), + charged: { + description: "3rd and 6th Antimatter Dimensions gain a power effect based on Infinities and Teresa level", + effect: () => chargedDimInfinityMult(), + formatEffect: value => formatPow(value, 4, 4) + } + }, + dim45mult: { + id: "45Mult", + cost: 1, + checkRequirement: () => InfinityUpgrade.dim27mult.isBought, + description: "4th and 5th Antimatter Dimensions gain a multiplier based on Infinities", + effect: () => dimInfinityMult(), + formatEffect: value => formatX(value, 1, 1), + charged: { + description: "4th and 5th Antimatter Dimensions gain a power effect based on Infinities and Teresa level", + effect: () => chargedDimInfinityMult(), + formatEffect: value => formatPow(value, 4, 4) + } + }, + resetBoost: { + id: "resetBoost", + cost: 1, + checkRequirement: () => InfinityUpgrade.dim36mult.isBought, + description: () => + `Decrease the number of Dimensions needed for Dimension Boosts and Antimatter Galaxies by ${formatInt(9)}`, + effect: 9, + charged: { + description: () => "Decrease Dimension Boost requirement based on Teresa level", + effect: () => 1 / (1 + Math.sqrt(Ra.pets.teresa.level) / 10), + formatEffect: value => `${formatX(value, 4, 4)}` + } + }, + buy10Mult: { + id: "dimMult", + cost: 1, + description: () => `Increase the multiplier for buying ${formatInt(10)} Antimatter Dimensions`, + effect: () => 1.1, + formatEffect: () => `${formatX(2, 0, 1)} ➜ ${formatX(2.2, 0, 1)}`, + charged: { + description: () => `The multiplier for buying ${formatInt(10)} Antimatter Dimensions gains ` + + "a power effect based on Teresa level", + effect: () => 1 + Ra.pets.teresa.level / 200, + formatEffect: value => formatPow(value, 3, 3) + } + }, + galaxyBoost: { + id: "galaxyBoost", + cost: 2, + checkRequirement: () => InfinityUpgrade.dim45mult.isBought, + description: "All Galaxies are twice as strong", + effect: 2, + charged: { + description: "All Galaxies are stronger based on Teresa level", + effect: () => 2 + Math.sqrt(Ra.pets.teresa.level) / 100, + formatEffect: value => `+${formatPercents(value - 1)}` + } + }, + thisInfinityTimeMult: { + id: "timeMult2", + cost: 3, + description: "Antimatter Dimensions gain a multiplier based on time spent in current Infinity", + effect: () => Decimal.max(Math.pow(Time.thisInfinity.totalMinutes / 4, 0.25), 1), + formatEffect: value => formatX(value, 2, 2), + charged: { + description: + "Antimatter Dimensions gain a power effect based on time spent in current Infinity and Teresa level", + effect: () => 1 + + Math.log10(Math.log10(Time.thisInfinity.totalMilliseconds + 100)) * + Math.sqrt(Ra.pets.teresa.level) / 150, + formatEffect: value => formatPow(value, 4, 4) + } + }, + unspentIPMult: { + id: "unspentBonus", + cost: 5, + checkRequirement: () => InfinityUpgrade.thisInfinityTimeMult.isBought, + description: "Multiplier to 1st Antimatter Dimension based on unspent Infinity Points", + effect: () => Currency.infinityPoints.value.dividedBy(2).pow(1.5).plus(1), + formatEffect: value => formatX(value, 2, 2), + charged: { + description: "Multiplier to 1st Antimatter Dimension based on unspent Infinity Points, powered by Teresa level", + effect: () => Currency.infinityPoints.value.dividedBy(2).pow(Math.sqrt(Ra.pets.teresa.level) * 1.5).plus(1), + formatEffect: value => formatX(value, 2, 2) + } + }, + dimboostMult: { + id: "resetMult", + cost: 7, + checkRequirement: () => InfinityUpgrade.unspentIPMult.isBought, + description: "Increase Dimension Boost multiplier", + effect: () => 2.5, + formatEffect: () => `${formatX(2, 0, 1)} ➜ ${formatX(2.5, 0, 1)}`, + charged: { + description: "Dimension Boost multiplier gains a power effect based on Teresa level", + effect: () => 1 + Ra.pets.teresa.level / 200, + formatEffect: value => formatPow(value, 3, 3) + } + }, + ipGen: { + id: "passiveGen", + cost: 10, + checkRequirement: () => InfinityUpgrade.dimboostMult.isBought, + description: () => `Passively generate Infinity Points ${formatInt(10)} times slower than your fastest Infinity`, + // Cutting corners: this is not actual effect, but it is totalIPMult that is displyed on upgrade + effect: () => (Teresa.isRunning || V.isRunning || Pelle.isDoomed ? DC.D0 : GameCache.totalIPMult.value), + formatEffect: value => { + if (Teresa.isRunning || V.isRunning) return "Disabled in this reality"; + if (Pelle.isDoomed) return "Disabled"; + const income = format(value, 2, 0); + const period = player.records.bestInfinity.time >= 999999999999 + ? "∞" + : Time.bestInfinity.times(10).toStringShort(); + return `${income} every ${period}`; + }, + charged: { + description: () => + `Gain Reality Machines each real-time second proportional to amount gained on Reality, + increasing with Teresa level`, + effect: () => Math.pow(Ra.pets.teresa.level, 2) * + Ra.unlocks.continuousTTBoost.effects.autoPrestige.effectOrDefault(1), + formatEffect: value => formatX(value, 2, 1) + } + }, + skipReset1: { + id: "skipReset1", + cost: 20, + description: () => + `Start every reset with ${formatInt(1)} Dimension Boost, automatically unlocking the 5th Antimatter Dimension`, + }, + skipReset2: { + id: "skipReset2", + cost: 40, + checkRequirement: () => InfinityUpgrade.skipReset1.isBought, + description: () => + `Start every reset with ${formatInt(2)} Dimension Boosts, automatically unlocking the 6th Antimatter Dimension`, + }, + skipReset3: { + id: "skipReset3", + cost: 80, + checkRequirement: () => InfinityUpgrade.skipReset2.isBought, + description: () => + `Start every reset with ${formatInt(3)} Dimension Boosts, automatically unlocking the 7th Antimatter Dimension`, + }, + skipResetGalaxy: { + id: "skipResetGalaxy", + cost: 300, + checkRequirement: () => InfinityUpgrade.skipReset3.isBought, + description: () => + `Start every reset with ${formatInt(4)} Dimension Boosts, automatically unlocking the 8th Antimatter Dimension; + and an Antimatter Galaxy`, + }, + ipOffline: { + id: "ipOffline", + cost: 1000, + checkRequirement: () => Achievement(41).isUnlocked, + description: () => (player.options.offlineProgress + ? `Only while offline, gain ${formatPercents(0.5)} of your best IP/min without using Max All` + : "This upgrade would give offline Infinity Point generation, but offline progress is currently disabled"), + effect: () => (player.options.offlineProgress + ? player.records.thisEternity.bestIPMsWithoutMaxAll.times(TimeSpan.fromMinutes(1).totalMilliseconds / 2) + : DC.D0), + isDisabled: () => !player.options.offlineProgress, + formatEffect: value => `${format(value, 2, 2)} IP/min`, + }, + ipMult: { + id: "ipMult", + cost: () => InfinityUpgrade.ipMult.cost, + checkRequirement: () => Achievement(41).isUnlocked, + costCap: DC.E6E6, + costIncreaseThreshold: DC.E3E6, + description: () => `Multiply Infinity Points from all sources by ${formatX(2)}`, + // Normally the multiplier caps at e993k or so with 3300000 purchases, but if the cost is capped then we just give + // an extra e7k to make the multiplier look nice + effect: () => (player.IPMultPurchases >= 3300000 ? DC.E1E6 : DC.D2.pow(player.IPMultPurchases)), + cap: () => Effarig.eternityCap ?? DC.E1E6, + formatEffect: value => formatX(value, 2, 2), + } +}; diff --git a/javascripts/core/secret-formula/news.js b/javascripts/core/secret-formula/news.js index c9eae6f85..5c5a73c81 100644 --- a/javascripts/core/secret-formula/news.js +++ b/javascripts/core/secret-formula/news.js @@ -1,5 +1,6 @@ -import { GameDatabase } from "./game-database.js"; -import { DC } from "../constants.js"; +import { DC } from "../constants"; + +import { GameDatabase } from "./game-database"; // A = always there // L = locked @@ -646,7 +647,7 @@ GameDatabase.news = [ }, { id: "a123", - text: "Finland declares that it's starting to import Cancer." + text: "Finland declares that it's starting to import Design." }, { id: "a124", @@ -691,15 +692,15 @@ GameDatabase.news = [ id: "a131", text: `Warning - We have just been informed that there is a chance of infection with a mind-virus of the Basilisk - type, similar to the infamous winking parrot. This particular example is known as 'Fractal Cancer Type III'. + type, similar to the infamous winking parrot. This particular example is known as 'Fractal Disease Type III'. This is believed to cause a 'crashing' of the mind, similar to a computer crash, due to the mathematical complexity of the image causing mathematical ideas that the mind can't comprehend, a Gondelian shock input eventually leading to crashing through Gondelian spoilers. All who have researched it have eventually died the same way, so it is impossible to tell exactly, but this is the common belief. Regardless, with the - introduction of 'cancer' mode, as well as reports of it's spontaneous appearance, sufficient repetition - of this mode's appearance may lead to an image forming in the mind similar to 'Fractal Cancer Type III'. + introduction of 'design' mode, as well as reports of it's spontaneous appearance, sufficient repetition + of this mode's appearance may lead to an image forming in the mind similar to 'Fractal Disease Type III'. With this in mind, we have some suggestions if you find yourself plagued with it. First, refresh immediately - and see if that fixes the issue. If not, navigate to options, and change the theme from cancer to literally + and see if that fixes the issue. If not, navigate to options, and change the theme from design to literally anything else. And above all else, Godspeed. We can't afford to lose anymore viewers.` }, { @@ -914,7 +915,7 @@ GameDatabase.news = [ id: "a174", text: `FREE RUNE ARMOR TRIMMING`, }, { @@ -929,7 +930,7 @@ GameDatabase.news = [ }, { id: "a176", - text: "I've been using cancer notation so long that I can actually read it now, please send help." + text: "I've been using emoji notation so long that I can actually read it now, please send help." }, { id: "a177", @@ -991,12 +992,12 @@ GameDatabase.news = [ { id: "a184", text: - `R̵̬̙͋͂̀̋͑̈́̇͠Ê̵͇͎͂̂̍̓̌̐̋̋̀̀̔M̶̨̲̯̘͙̬̥̮̣͚̱̫͛̽̃͌̚͝ - "Ą̴͍̝͐Į̷̛̲̯̫̘͌́̄̏͌̀̈́͝͝Ṅ̶̛̻̠̠̤̦̞̞͗̎̊̌̊͝͠ + `R̵̬̙͋͂̀̋͑̈́̇͠Ê̵͇͎͂̂̍̓̌̐̋̋̀̀̔M̶̨̲̯̘͙̬̥̮̣͚̱̫͛̽̃͌̚͝ + "Ą̴͍̝͐Į̷̛̲̯̫̘͌́̄̏͌̀̈́͝͝Ṅ̶̛̻̠̠̤̦̞̞͗̎̊̌̊͝͠ Ḁ̷̛͂̈́͗̎̃̓͛́͘ͅW̶̡̖͓̗̦̃̇̌̀͝A̵͇̭͉̓̎̈̿̊́̄̚͜R̶̝͚̲̭͎͇͎͓͖͚͇̀̈́͗̃̏̂̌͝͝Ę̴̡̤͙͈̝̬̰͒͘ ̶̺̈́́̆̓͘͘Ồ̸̢̢̮͓̯̗͙͚̬̉͊̿F̶̠̤̱̱̱͊̂̍̔̃͆̆̑̿͘ ̴̨̞̠̮͚̱͉͋̔͗̽̈́́́̅ͅỴ̶̣̙̹͚̲͔̲̼̬̥̀͌̒̾͘͘O̵̪̠̗̝̗̘̜͚̮̊͒͆̃̀̌̒͝ͅU̸͎͗̍̑̎̅̅͝R̵̗͑̽̏̓͆͒̈́͌͘̕ - ̸̑̽̇̆͊̔̍̊̈́̈́͘ͅS̸̘͐͝U̴̥̭̚͘R̸̖̜͍͒́̋͆̈́̓ + ='animation: a-text-grow 1s infinite'> ̶̺̈́́̆̓͘͘Ồ̸̢̢̮͓̯̗͙͚̬̉͊̿F̶̠̤̱̱̱͊̂̍̔̃͆̆̑̿͘ ̴̨̞̠̮͚̱͉͋̔͗̽̈́́́̅ͅỴ̶̣̙̹͚̲͔̲̼̬̥̀͌̒̾͘͘O̵̪̠̗̝̗̘̜͚̮̊͒͆̃̀̌̒͝ͅU̸͎͗̍̑̎̅̅͝R̵̗͑̽̏̓͆͒̈́͌͘̕ + ̸̑̽̇̆͊̔̍̊̈́̈́͘ͅS̸̘͐͝U̴̥̭̚͘R̸̖̜͍͒́̋͆̈́̓ R̸̡̛̛̪̝̟̱̣̹̭̟̣̀̈̀̏̉̌͝͠Õ̶͙͈͖̠͇̬͍̟̰U̵̩̫͉̝͔̼͎̦̔̓̽͌͊̏̇̓̀̓̀Ņ̸͍͇̘̙̥̰͉̲͕͈̥̍͛̃̑͝Ḑ̵̤̻̖̱̘̯̝̖̈̌̄̕͝ Ī̶̜̱̈́̑̃̉̄̋̔͐͋͠Ṅ̴͎̞͍̽͊͛̈́̅͛̈̅̚͠Ģ̸̢̾͊S̷̫̼̜̼͇̋͛̎͑͆̅̓̇`, }, @@ -1013,10 +1014,10 @@ GameDatabase.news = [ { id: "a186", text: - `/(^_^)/ \\(^_^)\\ /(^_^)/ \\(^_^)\\ /(^_^)/ \\(^_^)\\`, + `/(^_^)/ \\(^_^)\\ /(^_^)/ \\(^_^)\\ /(^_^)/ \\(^_^)\\`, }, { id: "a187", @@ -1042,10 +1043,10 @@ GameDatabase.news = [ }, { id: "a192", - text: - `17976931348623159077293051907890247336179769789423065727343008115773267580550096313270847732240753602112011 - 38798713933576587897688144166224928474306394741243777678934248654852763022196012460941194530829520850057688 - 38150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216` + // This ticker needs to be an unbroken string; using backtick strings and linebreaking will add spaces in the + // ticker itself where the linebreaks are + // eslint-disable-next-line max-len + text: "179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216" }, { id: "a193", @@ -1082,7 +1083,7 @@ GameDatabase.news = [ random *= 255; const color = `hsl(${random}, 90%, 60%)`; return `Disco Time!`; + animation: a-text-grow 0.4s infinite;'>Disco Time!`; }, }, { @@ -1351,8 +1352,8 @@ GameDatabase.news = [ id: "a238", get text() { return `AD Player: "How many orders of magnitude are you on?" Normal person: "Like, maybe 5 or 6 right now, my - dude." AD Player: "You are like a little baby. Watch this: C R O N C H"`; + dude." AD Player: "You are like a little baby. Watch this: C R O N C H"`; }, }, { @@ -1440,7 +1441,7 @@ GameDatabase.news = [ }, { id: "a246", - text: "OoooOOOOooOOO, it's me, the infamous news ghost!", + text: "OoooOOOOooOOO, it's me, the infamous news ghost!", }, (function() { let isFlipped = false; @@ -1504,7 +1505,7 @@ GameDatabase.news = [ { id: "a252", get text() { - return `This message is dilated.`; + return `This message is dilated.`; }, }, { @@ -2005,7 +2006,7 @@ GameDatabase.news = [ { id: "a321", text: - `It must be hard being on your phone all alone. But don't worry; we are still here. + `It must be hard being on your PC all alone. But don't worry; we are still here. Listening and watching your every step.` }, { @@ -2255,7 +2256,7 @@ GameDatabase.news = [ { id: "a352", get text() { - return ` + return ` This news message is antimemetic. You will forget that it exists shortly.`; } }, @@ -2837,7 +2838,7 @@ GameDatabase.news = [ and smote Slabdrill with his godlike power. As Slabdrill's corpse fell into the earth, he cried “ this will not be the last of me! Hevi will betr-“ and he fell in the Abyss of matter. Hevi gifted humanity with Eternity upgrades, which boosted infinity dimensions and time dimensions. And Hevi gave humanity his greatest - gift. EP multipliers. He said, these will multiply all EP gained by 5, but their cost will increase 25 times. + gift. EP multipliers. He said, these will multiply all EP gained by 5, but their cost will increase 50 times. Use them wisely. And Humanity journeyed off with their new power, as Slabdrill's words echoed in their heads.`, get unlocked() { return PlayerProgress.eternityUnlocked(); } }, @@ -5217,7 +5218,7 @@ GameDatabase.news = [ }, { id: "ai529", - text: "Cancer is said to be America's new measuring system for everything. What does this mean for us? We don't know. But we will know in 5 hours." + text: "Emoji is said to be America's new measuring system for everything. What does this mean for us? We don't know. But we will know in 5 hours." }, { id: "ai530", diff --git a/javascripts/core/secret-formula/progress-checker.js b/javascripts/core/secret-formula/progress-checker.js new file mode 100644 index 000000000..ba6681872 --- /dev/null +++ b/javascripts/core/secret-formula/progress-checker.js @@ -0,0 +1,152 @@ +import { GameDatabase } from "./game-database"; + +GameDatabase.progressStages = [ + /** + * This is used in both the catchup modal and for cloud save comparisons. Due to the fact that it's used for + * cloud comparisons, there's a lot of processing that needs to be done on raw player-like objects that aren't + * actually the player object itself. This means we can't take advantage of a lot of accessors and whatnot, and + * that many props which are normally Decimals are actually Strings at this point. + * @template + * { + * @property {Number} id Value corresponding to entry in PROGRESS_STAGE enum + * @property {String} name Name describing the stage of the game this entry is associated with + * @property {function: @return Boolean} hasReached Checking function for whether this stage has been + * reached; all checks are run in descending order, starting at the end of the list and moving upward. The + * last one checked (first entry) always returns true as a catch-all condition + * @property {String} suggestedResource A resource or multiple resources which may be + * useful for the player to aim for at this stage + * @property {function: @return Number} subProgressValue A value between 0 and 1 corresponding approximately + * to the progress within a stage. Values near 0 correspond to near the end of the previous stage and values + * near 1 correspond to near the start of the next stage; however in-between values are not an indicator of + * absolute progress and shouldn't be used as such + * } + */ + { + id: PROGRESS_STAGE.PRE_INFINITY, + name: "Before Infinity", + hasReached: () => true, + suggestedResource: "Antimatter", + // Galaxies are worth 1/3 each, boosts break ties within galaxies, and antimatter breaks ties within boosts + subProgressValue: save => 0.33 * save.galaxies + 0.02 * save.dimensionBoosts + + new Decimal(save.antimatter).log10() / 16000, + }, + { + id: PROGRESS_STAGE.EARLY_INFINITY, + name: "Infinity", + hasReached: save => new Decimal(save.infinities).gt(0), + suggestedResource: "Infinity Points", + // Half from infinity count, half from crunch autobuyer state + subProgressValue: save => Math.clampMax(new Decimal(save.infinities).toNumber(), 500) / 1000 + + Math.log10(150000 / player.auto.bigCrunch.interval) / 6.35, + }, + { + id: PROGRESS_STAGE.BREAK_INFINITY, + name: "Broken Infinity", + hasReached: save => save.auto.bigCrunch.interval <= 100, + suggestedResource: "Infinity Points", + subProgressValue: save => Math.sqrt(new Decimal(save.infinityPoints).log10() / 145), + }, + { + id: PROGRESS_STAGE.REPLICANTI, + name: "Replicanti", + hasReached: save => save.replicanti.unl, + suggestedResource: "Infinity Points", + subProgressValue: save => Math.sqrt((new Decimal(save.infinityPoints).log10() - 140) / 170), + }, + { + id: PROGRESS_STAGE.EARLY_ETERNITY, + name: "Eternity", + hasReached: save => new Decimal(save.eternities).gt(0), + suggestedResource: "Eternity Points", + subProgressValue: save => Math.sqrt(new Decimal(save.eternityPoints).pLog10() / 18), + }, + { + id: PROGRESS_STAGE.ETERNITY_CHALLENGES, + name: "Eternity Challenges", + hasReached: save => save.eternityChalls.eterc1 > 0, + suggestedResource: "Eternity Challenge Completions and Eternity Points", + // Half from ECs, half from EP (up to e1300) + subProgressValue: save => 0.008 * Object.values(save.eternityChalls).reduce((sum, c) => sum + c, 0) + + new Decimal(save.eternityPoints).log10() / 2500, + }, + { + id: PROGRESS_STAGE.EARLY_DILATION, + name: "Time Dilation", + hasReached: save => new Decimal(save.dilation.dilatedTime).gt(0), + suggestedResource: "Dilated Time", + subProgressValue: save => new Decimal(save.dilation.dilatedTime).log10() / 15, + }, + { + id: PROGRESS_STAGE.LATE_ETERNITY, + name: "Late Eternity", + hasReached: save => new Decimal(save.dilation.dilatedTime).gt(1e15), + suggestedResource: "Eternity Points and Dilated Time", + // Tracks up to e8000 even though many players will reality well before that; we still want to distinguish + // which saves are farther all the way up to the zeroth-reality RM cap + subProgressValue: save => Math.sqrt((new Decimal(save.eternityPoints).log10() - 1300) / 6700), + }, + { + id: PROGRESS_STAGE.EARLY_REALITY, + name: "Reality", + hasReached: save => save.realities > 0, + suggestedResource: "Reality Machines", + subProgressValue: save => Math.sqrt(new Decimal(save.reality.realityMachines).pLog10() / 6), + }, + { + id: PROGRESS_STAGE.TERESA, + name: "Teresa (1st Celestial)", + hasReached: save => save.celestials.teresa.quoteBits > 0, + suggestedResource: "Reality Machines", + subProgressValue: save => Math.log10(1 + save.celestials.teresa.pouredAmount) / 21, + }, + { + id: PROGRESS_STAGE.EFFARIG, + name: "Effarig (2nd Celestial)", + hasReached: save => save.celestials.effarig.quoteBits > 0, + suggestedResource: "Reality Machines and Relic Shards", + subProgressValue: save => Math.log10(1 + save.celestials.effarig.relicShards) / 14, + }, + { + id: PROGRESS_STAGE.ENSLAVED, + name: "The Nameless Ones (3rd Celestial)", + hasReached: save => save.celestials.enslaved.quoteBits > 0, + suggestedResource: "Reality Machines and Glyph Level", + subProgressValue: save => Math.sqrt((new Decimal(save.reality.realityMachines).log10() - 30) / 30), + }, + { + id: PROGRESS_STAGE.V, + name: "V (4th Celestial)", + hasReached: save => save.celestials.v.quoteBits > 0, + suggestedResource: "Number of V-Achievements", + subProgressValue: save => 0.0277 * Object.values(save.celestials.v.runUnlocks) + .reduce((total, ach) => total + ach, 0), + }, + { + id: PROGRESS_STAGE.RA, + name: "Ra (5th Celestial)", + hasReached: save => save.celestials.ra.quoteBits > 0, + suggestedResource: "Celestial Memories", + subProgressValue: save => Object.values(save.celestials.ra.pets).reduce((sum, pet) => sum + pet.level, 0) / 100, + }, + { + id: PROGRESS_STAGE.IMAGINARY_MACHINES, + name: "Imaginary Machines", + hasReached: save => save.reality.iMCap > 0, + suggestedResource: "Imaginary Machines", + subProgressValue: save => Math.log10(1 + save.reality.iMCap) / 9, + }, + { + id: PROGRESS_STAGE.LAITELA, + name: "Lai'tela (6th Celestial)", + hasReached: save => save.celestials.laitela.quoteBits > 0, + suggestedResource: "Dark Matter and Singularities", + subProgressValue: save => new Decimal(save.celestials.laitela.darkMatter).log10() / 308.25, + }, + { + id: PROGRESS_STAGE.PELLE, + name: "Pelle (7th Celestial)", + hasReached: save => save.celestials.pelle.doomed, + suggestedResource: "Remnants", + subProgressValue: save => Math.log10(1 + save.celestials.pelle.remnants) / 9, + }, +]; diff --git a/javascripts/core/secret-formula/reality/automator.js b/javascripts/core/secret-formula/reality/automator.js index a2ea46538..d8cc37cc0 100644 --- a/javascripts/core/secret-formula/reality/automator.js +++ b/javascripts/core/secret-formula/reality/automator.js @@ -1,74 +1,117 @@ -import { GameDatabase } from "../game-database.js"; +import { GameDatabase } from "../game-database"; GameDatabase.reality.automator = { + categoryNames: [ + "Time Studies", + "Event Triggers", + "Alter Settings", + "Information", + "Script Flow", + ], commands: [ { id: 0, isUnlocked: () => true, - keyword: "wait", - name: "wait - wait for something", - syntax: "wait condition", - description: "Forces Automator to wait for some condition or event", - sections: [ - { - name: "CONDITIONS", - items: [ - { - header: "resource comparison number", - description: ` - Wait until resource amount satisfies the comparison.
- Resources: am (Antimatter), ip (Infinity Points), ep (Eternity Points), dt (Dilated Time), - tp (Tachyon Particles), rg (Replicanti Galaxies), rep/replicanti, tt/time theorems, total tt
- Comparisons: <, <=, > >=
- Number should be in scientific format, e.g. 1000, 1e100, 1.8e308 - ` - }, - { - header: "pending completions comparison number", - description: ` - Wait for a certain total number of EC completions that you'd get at eternity. - ` - }, - { - header: "ECnumber completions comparison number", - description: ` - Wait for a certain total number of completions of that EC. - ` - }, - { - header: "prestige", - description: ` - Wait until that prestige has been triggered.
- Prestiges: infinity, eternity, reality - ` - } - ] - } - ], + keyword: "STUDIES RESPEC", + category: 0, + syntax: `studies respec`, + description: `This command turns on the respec option, which will respec your Time Studies on the next manual or + automatic Eternity. Note that this does not actually perform an Eternity on its own; make sure your Autobuyer + is on or you manually run the ETERNITY command (although ETERNITY has its own built-in respec option).`, examples: [ - "wait infinity", - "wait am >= 1e308", - "wait pending completions >= 5", - "wait ec9 completions >= 4", + `studies respec`, ] }, { id: 1, isUnlocked: () => true, - keyword: "start", - name: "start - start Eternity Challenge (this also unlocks said EC) or Time Dilation", - syntax: "start [ecX|dilation]", + keyword: "STUDIES LOAD", + category: 0, + syntax: `studies [nowait] load id selector
+ studies [nowait] load name name`, + description: `Loads a Time Study preset, as if you had clicked on the button in the Time Study tab.`, + sections: [ + { + name: "INPUTS", + items: [ + { + header: "nowait", + description: ` + If present, the Automator will purchase as many studies as possible before continuing onward. By default + (ie. without "nowait") this command will repeat this line indefinitely until all of the studies in the + preset are bought; this may cause the Automator to get stuck indefinitely if you are not careful. + ` + }, + { + header: "selector", + description: ` + Finds and loads the specified Time Study preset by its slot number. This is numbered one through six, + ordered from left to right.` + }, + { + header: "name", + description: "Finds and loads the specified Time Study preset by its given name. This is case-sensitive." + }, + ] + } + ], examples: [ - "start ec12", - "start dilation" + `studies load id 2`, + `studies load name ANTI`, + `studies nowait load name dil`, ] }, { id: 2, isUnlocked: () => true, - keyword: "infinity / eternity / reality", - name: "infinity|eternity|reality - triggers Infinity, Eternity, or Reality", - syntax: "infinity [nowait],
eternity [nowait] [respec],
reality [nowait] [respec]", + keyword: "STUDIES PURCHASE", + category: 0, + syntax: `studies [nowait] purchase study_list`, + description: "Purchase Time Studies specified from a list of Time Studies.", + sections: [ + { + name: "INPUTS", + items: [ + { + header: "nowait", + description: ` + If present, the Automator will purchase as many studies as possible before continuing onward. By default + (ie. without "nowait") this command will repeat this line indefinitely until all of the studies in the + preset are bought; this may cause the Automator to get stuck indefinitely if you are not careful. + ` + }, + { + header: "study_list", + description: ` + The exported Time Study tree format is supported here, which is simply a list of Time Study IDs + separated by commas. This command also supports a more flexible formatting, additionally allowing + ranges of studies (for example, 11-62) and the following aliases:
+
antimatter, infinity, time, active, passive, idle, light, dark
+ A variable name may be used in place of the entire Time Study list as well (see the definition panel), + although in that case the shorthand ranges and aliases are not allowed.` + }, + ] + } + ], + examples: [ + "studies nowait purchase 11,21,31", + "studies purchase 11-62, antimatter, 111, idle", + "studies nowait purchase ec6Studies", + ] + }, + { + id: 3, + isUnlocked: () => true, + keyword: "PRESTIGE", + category: 1, + syntax: ` + infinity [nowait]
+ eternity [nowait] [respec]
+ reality [nowait] [respec]`, + description: `Triggers an Infinity, Eternity, or Reality reset if possible, otherwise the automator will wait at + this command until it becomes possible. If you find that your script often gets stuck on this command, an + Autobuyer may be triggering a prestige before the Automator reaches this line - consider using nowait or + adjusting your Autobuyer settings using AUTO.`, sections: [ { name: "MODIFIERS", @@ -76,14 +119,16 @@ GameDatabase.reality.automator = { { header: "nowait", description: ` - If it is not possible to prestige, move on to the next command. + If present, the Automator will move on to the next command instead of repeatedly trying on this + command in situations where the prestige is not possible (eg. within an EC below the goal). ` }, { header: "respec", description: ` - Eternity: respec Time Studies and Eternity.
- Reality: unequip Glyphs and Reality. + For non-Infinity prestiges, also does the related respec action when triggering prestige. + Eternity: Respec Time Studies and Eternity.
+ Reality: Unequip Glyphs and Reality. ` }, ] @@ -95,77 +140,13 @@ GameDatabase.reality.automator = { "reality nowait", ] }, - { - id: 3, - isUnlocked: () => true, - keyword: "tt", - name: "tt - purchases Time Theorems with a resource or buys one of each resource", - syntax: - "tt action", - sections: [ - { - name: "ACTIONS", - items: [ - { - header: "resources", - description: ` - Buys with a specific resource.
- Resources: am (Antimatter), ip (Infinity Points), ep (Eternity Points) - ` - }, - { - header: "all", - description: ` - Buys one of each time theorem from all resources. - ` - }, - ] - } - ] - }, { id: 4, - isUnlocked: () => BlackHole(1).isUnlocked, - keyword: "black hole", - name: "black hole - turns the Black Hole on and off", - syntax: "black hole on/off", - examples: [ - "black hole on", - "black hole off", - ] - }, - { - id: 5, - isUnlocked: () => Enslaved.isUnlocked, - keyword: "store time", - name: "store time - either turns on/off the storing of time or can be used to use stored time", - syntax: "store time action", - sections: [ - { - name: "ACTIONS", - items: [ - { - header: "on/off", - description: ` - Turns storing time on or off. - ` - }, - { - header: "use", - description: ` - Uses stored time. - ` - } - ] - } - ] - }, - { - id: 6, isUnlocked: () => true, - keyword: "unlock", - name: "unlock - can be used to unlock certain features", - syntax: "unlock [nowait] feature", + keyword: "UNLOCK", + category: 1, + syntax: "unlock [nowait] feature", + description: "Unlocks the specified Eternity Challenge or Time Dilation.", sections: [ { name: "MODIFIERS", @@ -173,7 +154,8 @@ GameDatabase.reality.automator = { { header: "nowait", description: ` - If it is not possible to unlock, move on to the next command. + If present, the Automator will move on to the next command even if unlocking the feature fails. By + default, the Automator will keep running this command until the unlock succeeds. ` }, ] @@ -181,38 +163,60 @@ GameDatabase.reality.automator = { ], examples: [ "unlock dilation", - "unlock ecX" + "unlock ec7" ] }, { - id: 7, + id: 5, isUnlocked: () => true, - keyword: "auto", - name: "auto - turn Infinity/Eternity/Reality autobuyers on or off and change their modes", - syntax: "auto [infinity|eternity|reality] [setting]", + keyword: "START", + category: 1, + syntax: ` + start ecN
+ start dilation`, + description: `Start a specified Eternity Challenge or a Dilated Eternity. This command will also attempt + to unlock the EC if not unlocked, but will not do the same for Dilation (use UNLOCK command to do that). + If you are already in the specified EC or Dilated Eternity, running this command again will do nothing; + otherwise, the Automator will keep attempting to start the Eternity until it succeeds.`, + examples: [ + "start ec12", + "start dilation" + ] + }, + { + id: 6, + isUnlocked: () => true, + keyword: "AUTO", + category: 2, + syntax: `auto infinity [setting]
+ auto eternity [setting]
+ auto reality [setting]`, + description: `Turns prestige Autobuyers on or off and allows you to change their settings. If the setting option + is not present, this command will toggle the Autobuyer state, turning it off if it is on and turning it on if + it is off. This command will not work if you try to modify an Autobuyer or setting you do not have.`, sections: [ { name: "SETTINGS", items: [ { header: "on | off", - description: "Turns autobuyer on or off.", + description: "Turns specified Autobuyer on or off.", }, { - header: "number time units", - description: `Usable with infinity/eternity only. - Turns the autobuyer on and set it to trigger at the given interval` + header: "number time units", + description: `Usable with Infinity and Eternity only. + Turns the Autobuyer on and set it to trigger at the given interval.` }, { - header: "number x highest", - description: `Usable with infinity/eternity only. Turns the autobuyer on and sets it to - "X times highest" mode` + header: "number x highest", + description: `Usable with Infinity and Eternity only. Turns the Autobuyer on and sets it to + "X times highest" mode.` }, { - header: "number currency", - description: `Turns the autobuyer on and sets it to trigger at a specific amount. The currency must - match the autobuyer type (ip, ep, or rm). For the reality autobuyer, this will select "reality - machines" mode`, + header: "number currency", + description: `Turns the Autobuyer on and sets it to trigger at a specific amount. The currency must + match the autobuyer type (IP, EP, or RM). This will select "Reality Machines" mode for the Reality + Autobuyer. Glyph Level mode cannot be changed or set via the Automator, only manually.`, }, ] } @@ -226,206 +230,59 @@ GameDatabase.reality.automator = { ] }, { - id: 8, - isUnlocked: () => true, - keyword: "if", - name: "if - compares your amount to the game's amount of something, such as a currency", - syntax: `if [am|ip|ep|dt|tp|rg|rep|infinities|banked infinities|eternities|realities|tt|total tt| - pending glyph level|pending completions|ec[number] completions] (comparison) [number]`, + id: 7, + isUnlocked: () => BlackHole(1).isUnlocked, + keyword: "BLACK HOLE", + category: 2, + syntax: "black hole state", + description: `Toggles the speedup effect from the Black Hole on or off. Turning the Black Hole on via the + Automator does not bypass the gradual acceleration from off to max speed which occurs before they are + permanent.`, examples: [ - "if ep <= 1e3000", - "if dt >= 1e50", - "if ip < 1e1500000", - "if ec10 completions < 5" + "black hole on", + "black hole off", + ] + }, + { + id: 8, + isUnlocked: () => Enslaved.isUnlocked, + keyword: "STORE GAME TIME", + category: 2, + syntax: "store game time action", + description: `Changes whether or not the Black Hole is storing time. Also allows usage of stored time.`, + sections: [ + { + name: "ACTIONS", + items: [ + { + header: "on | off", + description: ` + Turns storing game time on or off. + ` + }, + { + header: "use", + description: ` + Uses all stored game time. Does not alter the on/off state of time storage. + ` + } + ] + } + ], + examples: [ + "store time on", + "store time off", + "store time use", ] }, { id: 9, isUnlocked: () => true, - keyword: "pause", - name: "pause - pauses the Automator for a set amount of time", - syntax: "pause [interval]", - examples: [ - "pause 10s", - "pause 1 minute", - "pause 34 seconds" - ], - sections: [ - { - name: "OTHER", - items: [ - { - header: "Undesirable effects", - description: `This command may behave undesirably when it runs during offline progress due to limited - tick count. A 1-second pause that is usually 20-30 ticks might be only 1 game tick when processing 8 - hours of offline progress, which might not be enough for the resources needed for the next line of the - script`, - }, - { - header: "Alternatives", - description: "Using something like 'wait' will allow you to set it for a certain resource amount." - } - ] - } - ] - }, - { - id: 10, - isUnlocked: () => true, - keyword: "until", - name: "until - repeats commands until a condition or event", - syntax: `until [condition | event] {
-
commands
- }
- condition: [quantity] (comparison) [number]
- quantity: [am|ip|ep|dt|tp|rg|rep|tt|total tt|pending completions|ecx completions]
- event: [infinity|eternity|reality] (can happen at any time after loop starts)`, - description: `Commands are repeated; the condition is checked at the start and every - time the loop repeats. If an event is specified, then the loop will repeat until the - event occurs. (The event has to happen after the loop begins).
- A variable name may be used in place of number, see define`, - examples: [ - `until ep > 1e500 {
-
- tt all
- studies nowait 11-62
- }`, - ] - }, - { - id: 11, - isUnlocked: () => true, - keyword: "while", - name: "while - repeats commands while a condition is met", - syntax: `while [quantity] (comparison) [number]{
-
commands
- }
- quantity: [am|ip|ep|dt|tp|rg|rep|tt|total tt|pending completions|ec[number] completions]
- comparison: [<|<=|>=|>]
- number: Number in normal or scientific notation`, - description: `Commands are repeated; the condition is checked at the start and every - time the loop repeats.
- A variable name may be used in place of number, see define`, - examples: [ - `while ep < 1e500 {
-
- tt all
- studies nowait 11-62
- }`, - `while myThreshold > am { ...`, - ] - }, - { - id: 12, - isUnlocked: () => true, - keyword: "studies respec", - name: "studies respec - respec Time Studies on next Eternity", - syntax: `studies respec`, - examples: [ - `studies respec`, - ] - }, - { - id: 13, - isUnlocked: () => true, - keyword: "studies load preset", - name: "studies load preset - Load a saved Time Study preset", - syntax: `studies [nowait] load preset [name | number]`, - description: `Loads a Time Study preset, as if you'd clicked on the button in the Time Study tab. - Number is 1 to 6 (corresponding to slot), but the given name can also be used. - If nowait is present, then the Automator will purchase as many Time Studies as - possible from the preset before moving on to the next command, even if some cannot be bought.
- If nowait is not present, then the Automator will repeatedly attempt to buy the studies in order. - It will repeat this line indefinitely until all of the studies in the preset are bought; the automator may get - stuck indefinitely if the preset contains studies in a configuration which cannot be bought simultaneously, - for example 71, 72, and 73 before the Dimension Split Dilation upgrade is purchased.`, - examples: [ - `studies load preset 2`, - `studies nowait load preset dil`, - ] - }, - { - id: 14, - isUnlocked: () => true, - keyword: "studies", - name: "studies - Purchase Time Studies", - syntax: `studies [nowait] [study list]`, - description: `Purchase Time Studies specified from a list formatted like a Tree export. - If nowait is present, then the Automator will purchase as many Time Studies as - possible from the text string before moving on to the next command, even if some cannot be bought.
- If nowait is not present, then the Automator will repeatedly attempt to buy the studies in order. - It will repeat this line indefinitely until all of the studies in the preset are bought; the automator may get - stuck indefinitely if the preset contains studies in a configuration which cannot be bought simultaneously, - for example 71, 72, and 73 before the Dimension Split Dilation upgrade is purchased.
- The Time Study list here has more flexibility and can consist of Time Study numbers, separated by - spaces or commas, ranges of studies (for example, 11-62) and the following aliases:
-
antimatter, infinity, time, active, passive, idle, light, dark
- A variable name may be used in place of Time Study list, see define.`, - examples: [ - "studies nowait 11,21,31", - "studies 11-62, antimatter, 111, idle", - "studies nowait ec6Studies", - ] - }, - { - id: 15, - isUnlocked: () => true, - keyword: "define", - name: "define - defining constants", - syntax: `define [constant_name] = [constant]`, - description: `Define constants for either numbers or Time Study tree imports`, - examples: [ - "define inf = 1e308", - "define studytree = 11,21,22,31,32,33" - ] - }, - { - id: 16, - isUnlocked: () => true, - keyword: "currencies", - name: "List of currencies", - syntax: "You can use these in any if, while, until, or wait command.", - description: `This is a list of "currencies" or numbers that you can use.
- Note that when used, most currencies will need to be in scientific notation.
- am - antimatter amount
- ip - current infinity point amount
- ep - current eternity point amount
- rm - current reality machine amount
- infinities - current infinity amount
- banked infinities - current banked infinity amount
- eternities - current eternity amount
- realities - current reality amount
- pending ip - IP gained on crunch (0 if not available)
- pending ep - EP gained on eternity (0 if not available)
- pending tp - TP gained on exiting dilation
- pending rm - RM gained on reality (0 if not available)
- pending glyph level - glyph level gained on reality (0 if not available)
- dt - dilated time amount
- tp - tachyon particle amount
- rg - replicanti galaxy amount (does not use scientific)
- rep - replicanti amount
- tt - time theorem amount
- total tt - TOTAL time theorems, includes all forms of generated TT
- total completions - total completions of all eternity challenges
- pending completions - total completions of current EC at eternity
- ec[number] completions - amount of EC completions for a certain EC
- `, - examples: [ - `if total tt >= 5 -
commands
- `, - `while ec10 completions >= 1 -
commands
` - ] - }, - { - id: 17, - isUnlocked: () => true, - keyword: "notify", - name: "notify - send a progress notification", - syntax: "notify \"text\"", + keyword: "NOTIFY", + category: 3, + syntax: "notify \"text\"", description: `Takes the specified text and posts it in the top-right corner as - a text notification, in the same spot as other notifications such as auto-save + a text notification, in the same spot and style as other notifications such as auto-save and achievement/upgrade unlocks. Can be useful for seeing automator status while on tabs other than the Automator tab.`, examples: [ @@ -434,20 +291,298 @@ GameDatabase.reality.automator = { ] }, { - id: 18, + id: 10, isUnlocked: () => true, - keyword: "(Comments)", - name: "#|// - leaves a comment in your script", + keyword: "Adding Comments", + category: 3, syntax: "# text
// text", description: `Allows you to leave a note to yourself within your script. This may be useful for organizing or keeping track of which parts of your script do various things, - in a way that appears more readable than just the commands. These commands will do nothing - positive or negative for the automator's functionality, and only serve as a tool to + in a way that appears more readable than just the commands. These commands mainly serve as a tool to help you keep the steps of your scripts easier to follow if desired.`, + sections: [ + { + name: "NOTES", + items: [ + { + header: "Inline comments", + description: ` + The Automator does not support comments which are placed after an already functional + line of code, on the same line. As an example, the single line "studies load name TDI // Load push" + will be an invalid command. In this case, you will need to move the comment to a separate line + in the automator. + ` + }, + { + header: "Execution speed", + description: ` + Having comments will not slow down your script, as they are completely skipped during + execution and do not count as a command for the purposes of running. For example, even if you have + a really long explanation in the form of comments on lines 20-40, the Automator will still + immediately skip from line 19 to 41 during execution. + ` + }, + ] + } + ], examples: [ "# get 1e20 before starting ec1", "// this loop alternates dilation and pushing" ] }, + { + id: 11, + isUnlocked: () => true, + keyword: "WAIT", + category: 4, + syntax: "wait condition", + description: `Forces Automator to wait for some condition or event. To wait for a certain duration of time, + use the PAUSE command instead.`, + sections: [ + { + name: "CONDITIONS", + items: [ + { + header: "comparison", + description: ` + Wait until the comparison statement is true. Check the entry for "Formatting Comparisons" for details + on how to properly input this option. + ` + }, + { + header: "prestige", + description: ` + Wait until the specified prestige (Infinity, Eternity, or Reality) has been triggered by its respective + Autobuyer. This must happen after this command is reached; if the Autobuyer triggers + before the command is reached, your script may get stuck. + ` + } + ] + } + ], + examples: [ + "wait am >= 1e308", + "wait pending completions >= 5", + "wait ec9 completions >= 4", + "wait infinity", + ] + }, + { + id: 12, + isUnlocked: () => true, + keyword: "PAUSE", + category: 4, + syntax: "pause interval", + description: `Tells the automator to stop moving forward and executing commands for a certain amount of time. + Note that if the pause duration is shorter than the automator's execution speed, the automator will wait until + the next execution tick before moving on.`, + examples: [ + "pause 10s", + "pause 1 minute", + "pause 34 seconds" + ], + sections: [ + { + name: "INTERVAL FORMATTING", + items: [ + { + header: "Specified Interval", + description: `This command accepts time units of milliseconds ("ms"), seconds ("s", "sec", or "seconds"), + minutes ("m", "min", or "minutes"), and hours ("h" or "hours"). You cannot provide just a number and + nothing else; a unit of time must be specified.`, + }, + { + header: "Defined Constant", + description: `A defined constant may be used instead, see the definition panel. The defined value will + be assumed to be in units of seconds.` + }, + ] + }, + { + name: "OTHER", + items: [ + { + header: "Offline Side-effects", + description: `This command may behave undesirably when it runs during offline progress due to limited + tick count. A 1-second pause that is usually 20-30 ticks might be only 1 game tick when processing + hours of offline progress, which might not be enough for the resources needed for the rest of the + script.`, + }, + { + header: "Alternatives", + description: `Using another command like 'WAIT' will allow you to set it for a certain resource amount, + in order to ensure that the game has the proper state before moving onward.` + }, + { + header: "Manual Skip", + description: `You can manually force the Automator to continue execution past a PAUSE command without + waiting the entire specified time by stepping forward one line (to put it on the next one) and then + resuming execution. If you find yourself doing this regularly, consider modifying your script.` + } + ] + } + ] + }, + { + id: 13, + isUnlocked: () => true, + keyword: "IF", + category: 4, + syntax: `if condition {
+
commands
+ }`, + description: `Defines an inner block of block of the automator script which will only be executed if the specified + comparison is true when this line is reached. If the comparison is false, the automator will instead skip to the + first line after the block and continue execution from there.`, + examples: [ + "if ec10 completions < 5", + "if ep > 1e6000" + ] + }, + { + id: 14, + isUnlocked: () => true, + keyword: "UNTIL", + category: 4, + syntax: `until comparison {
+
commands
+ }
until prestige_event {
+
commands
+ }`, + description: `Defines an inner block of the script where commands are repeated; the comparison is checked at the + start and every time the loop repeats. If the condition is false when the UNTIL statement is first reached, the + inner block of commands will be skipped entirely. +

+ If an prestige event (ie. Infinity, Eternity, or Reality) is specified instead of a condition, then the block + will always be entered and the commands within the block will repeat until the event occurs for the first time + after entering the block. Note that the Automator will finish the rest of the loop and then exit after + the prestige event occurs - it will not immediately exit the loop in the middle.`, + examples: [ + "until ep > 1e500", + "until reality", + ] + }, + { + id: 15, + isUnlocked: () => true, + keyword: "WHILE", + category: 4, + syntax: `while comparison {
+
commands
+ }`, + description: `Defines an inner block of the script where commands are repeated; the comparison is checked at the + start and every time the loop repeats. If the condition is false when the WHILE statement is first reached, the + inner block of commands will be skipped entirely.`, + examples: [ + `while ep < 1e500`, + `while myThreshold > am`, + ] + }, + { + id: 16, + isUnlocked: () => true, + keyword: "Currency List", + category: 4, + syntax: "You can use these in any IF, WHILE, UNTIL, or WAIT command", + description: `This is a list of "currencies" or numbers that you can use within the Automator.
+ Note that when used, most currencies will need to be in scientific notation.
+ am - Current Antimatter amount
+ ip - Current Infinity Point amount
+ ep - Current Eternity Point amount
+ rm - Current Reality Machine amount
+ infinities - Current Infinity amount
+ banked infinities - Current Banked Infinity amount
+ eternities - Current Eternity amount
+ realities - Current Reality amount
+ pending ip - IP gained on Infinity (0 if not available)
+ pending ep - EP gained on Eternity (0 if not available)
+ pending tp - TP gained on exiting Dilation
+ pending rm - RM gained on Reality (0 if not available)
+ pending glyph level - Glyph Level gained on Reality (0 if not available)
+ dt - Current Dilated Time amount
+ tp - Current Tachyon Particle amount
+ rg - Current Replicanti Galaxy amount (does not use scientific)
+ rep - Current Replicanti amount
+ tt - Current Time Theorem amount
+ total tt - TOTAL Time Theorems, includes all forms of generated TT and any spent on Studies
+ total completions - Total completions of all Eternity Challenges
+ pending completions - Total completions of current EC at Eternity
+ ecX completions - Amount of EC completions for a certain EC (eg. "ec6 completions")
+ ` + }, + { + id: 17, + isUnlocked: () => true, + keyword: "Formatting Comparisons", + category: 4, + syntax: "resource1 condition resource2", + description: ` + Comparisons are used within certain commands, which allow you to control the behavior of the automator based + on the game's current state. They have a standard format with two value inputs and a comparison operator, but + the value inputs can be anything as long as it is formatted correctly overall.`, + sections: [ + { + name: "CONDITIONS", + items: [ + { + header: "resource", + description: ` + This can be any Automator Currency, a defined constant, or a number which must be formatted in + scientific notation (eg. 1000, 1e100, 1.8e308). Unlike more general programming languages, this must + be a single value (ie. math expressions such as "ip + pending ip" are not allowed). + ` + }, + { + header: "condition", + description: ` + This must be an inequality operator (<, <=, >, >=), which takes on its typical mathematical meaning. + Equality operators (==, !=) are not allowed, as the nature of the game means that numbers will often + never be exactly equal and thus checking based on direct equality may lead to unexpected script + behavior. + ` + }, + ] + } + ], + examples: [ + "ep < 1e20", + "total tt > 14000", + ] + }, + { + id: 18, + isUnlocked: () => true, + keyword: "Commands with inner blocks", + category: 4, + syntax: `header_command {
+
inner_commands
+ }`, + description: `Some commands are associated with an "inner block" of commands. This inner block can contain still + contain any other valid command, but may or may not actually get executed based on what the state of the game is + when header_command is executed. This allows you to repeat some commands over and over (eg. Time Study + purchasing), or to skip them entirely (eg. not entering an EC if it already has full completions). These blocks + can be nested if desired, with inner blocks being placed within one another. +

+ In the text editor mode: Specify the inner block with curly braces, with the opening brace { on the same line as + the comparison and the closing brace } on its own line after the last line you want inside the block. Inner + commands do not need to be indented, although it may be visually helpful to do so. +

+ In the block editor mode: These commands come with an empty dotted rectangle which indicates which commands are + within the inner block. Subsequent blocks can then be dragged inside the dotted rectangle. + `, + examples: [ + `if ec10 completions < 5 {
+
+ unlock ec10
+ start ec10
+ }`, + `until ep > 1e8 {
+
+ studies nowait purchase 11-62
+ pause 10s
+ eternity respec
+ }` + ] + }, ] }; diff --git a/javascripts/core/secret-formula/reality/glyph-effects.js b/javascripts/core/secret-formula/reality/glyph-effects.js new file mode 100644 index 000000000..50912a468 --- /dev/null +++ b/javascripts/core/secret-formula/reality/glyph-effects.js @@ -0,0 +1,688 @@ +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; + +export const GlyphCombiner = Object.freeze({ + /** + * @param {number[]} x + * @returns {number} + */ + add: x => x.reduce(Number.sumReducer, 0), + /** + * @param {number[]} x + * @returns {number} + */ + multiply: x => x.reduce(Number.prodReducer, 1), + /** + * For exponents, the base value is 1, so when we add two exponents a and b we want to get a + b - 1, + * so that if a and b are both close to 1 so is their sum. In general, when we add a list x of exponents, + * we have to add 1 - x.length to the actual sum, so that if all the exponents are close to 1 the result + * is also close to 1 rather than close to x.length. + * @param {number[]} x + * @returns {number} + */ + addExponents: x => x.reduce(Number.sumReducer, 1 - x.length), + /** + * @param {Decimal[]} x + * @returns {Decimal} + */ + multiplyDecimal: x => x.reduce(Decimal.prodReducer, DC.D1) +}); + + +GameDatabase.reality.glyphEffects = { + timepow: { + id: "timepow", + bitmaskIndex: 0, + isGenerated: true, + glyphTypes: ["time"], + singleDesc: "Time Dimension power +{value}", + totalDesc: "Time Dimension multipliers ^{value}", + shortDesc: "TD power +{value}", + effect: (level, strength) => 1.01 + Math.pow(level, 0.32) * Math.pow(strength, 0.45) / 75, + formatEffect: x => format(x, 3, 3), + formatSingleEffect: x => format(x - 1, 3, 3), + combine: GlyphCombiner.addExponents, + enabledInDoomed: true, + }, + timespeed: { + id: "timespeed", + bitmaskIndex: 1, + isGenerated: true, + glyphTypes: ["time"], + singleDesc: "Multiply game speed by {value}", + totalDesc: "Game runs ×{value} faster", + genericDesc: "Game speed multiplier", + shortDesc: "Game speed ×{value}", + effect: (level, strength) => (GlyphAlteration.isEmpowered("time") + ? 1 + Math.pow(level, 0.35) + : 1 + Math.pow(level, 0.3) * Math.pow(strength, 0.65) / 20), + formatEffect: x => format(x, 3, 3), + combine: GlyphCombiner.multiply, + alteredColor: () => GlyphAlteration.getEmpowermentColor("time"), + alterationType: ALTERATION_TYPE.EMPOWER, + enabledInDoomed: true, + }, + timeetermult: { + id: "timeetermult", + bitmaskIndex: 2, + isGenerated: true, + glyphTypes: ["time"], + singleDesc: "Multiply Eternity gain by {value}", + totalDesc: "Eternity gain ×{value}", + genericDesc: "Eternity gain multiplier", + shortDesc: "Eternities ×{value}", + effect: (level, strength) => Math.pow((strength + 3) * level, 0.9) * + Math.pow(3, GlyphAlteration.sacrificeBoost("time")), + formatEffect: x => format(x, 2, 2), + combine: GlyphCombiner.multiply, + alteredColor: () => GlyphAlteration.getBoostColor("time"), + alterationType: ALTERATION_TYPE.BOOST + }, + timeEP: { + id: "timeEP", + bitmaskIndex: 3, + isGenerated: true, + glyphTypes: ["time"], + singleDesc: () => (GlyphAlteration.isAdded("time") + ? "Eternity Point gain \n×{value} [and ^]{value2}" + : "Multiply Eternity Point gain by {value}"), + totalDesc: () => (GlyphAlteration.isAdded("time") + ? "Eternity Point gain ×{value} and ^{value2}" + : "Eternity Point gain ×{value}"), + genericDesc: () => (GlyphAlteration.isAdded("time") + ? "Eternity Point gain multiplier and power" + : "Eternity Point gain multiplier"), + shortDesc: () => (GlyphAlteration.isAdded("time") + ? "EP ×{value} and ^{value2}" + : "EP ×{value}"), + effect: (level, strength) => Math.pow(level * strength, 3) * 100, + formatEffect: x => format(x, 2, 3), + combine: GlyphCombiner.multiply, + conversion: x => 1 + Math.log10(x) / 1000, + formatSecondaryEffect: x => format(x, 4, 4), + alteredColor: () => GlyphAlteration.getAdditionColor("time"), + alterationType: ALTERATION_TYPE.ADDITION + }, + dilationDT: { + id: "dilationDT", + bitmaskIndex: 4, + isGenerated: true, + glyphTypes: ["dilation"], + singleDesc: "Multiply Dilated Time gain by {value}", + totalDesc: "Dilated Time gain ×{value}", + shortDesc: "DT ×{value}", + effect: (level, strength) => (GlyphAlteration.isEmpowered("dilation") + ? DC.D1_005.pow(level).times(15) + : Decimal.pow(level * strength, 1.5).times(2)), + formatEffect: x => format(x, 2, 1), + combine: GlyphCombiner.multiplyDecimal, + alteredColor: () => GlyphAlteration.getEmpowermentColor("dilation"), + alterationType: ALTERATION_TYPE.EMPOWER + }, + dilationgalaxyThreshold: { + id: "dilationgalaxyThreshold", + bitmaskIndex: 5, + isGenerated: true, + glyphTypes: ["dilation"], + singleDesc: "Tachyon Galaxy threshold multiplier ×{value}", + genericDesc: "Tachyon Galaxy cost multiplier", + shortDesc: "TG threshold ×{value}", + effect: (level, strength) => 1 - Math.pow(level, 0.17) * Math.pow(strength, 0.35) / 100 - + GlyphAlteration.sacrificeBoost("dilation") / 50, + formatEffect: x => format(x, 3, 3), + alteredColor: () => GlyphAlteration.getBoostColor("dilation"), + alterationType: ALTERATION_TYPE.BOOST, + combine: effects => { + const prod = effects.reduce(Number.prodReducer, 1); + return prod < 0.4 + ? { value: 0.4 - Math.pow(0.4 - prod, 1.7), capped: true } + : { value: prod, capped: false }; + }, + enabledInDoomed: true, + }, + dilationTTgen: { + // TTgen slowly generates TT, value amount is per second, displayed per hour + id: "dilationTTgen", + bitmaskIndex: 6, + isGenerated: true, + glyphTypes: ["dilation"], + singleDesc: () => (GlyphAlteration.isAdded("dilation") + ? "Generates {value} Time Theorems/hour \n[and multiplies Time Theorem \ngeneration by] {value2}" + : "Generates {value} Time Theorems per hour"), + totalDesc: () => (GlyphAlteration.isAdded("dilation") + ? "Generating {value} Time Theorems/hour and Time Theorem generation ×{value2}" + : "Generating {value} Time Theorems per hour"), + genericDesc: () => (GlyphAlteration.isAdded("dilation") + ? "Time Theorem generation and multiplier" + : "Time Theorem generation"), + shortDesc: () => (GlyphAlteration.isAdded("dilation") + ? "{value} TT/hr and TTgen ×{value2}" + : "{value} TT/hr"), + effect: (level, strength) => Math.pow(level * strength, 0.5) / 10000, + /** @type {function(number): string} */ + formatEffect: x => format(3600 * x, 2, 2), + combine: GlyphCombiner.add, + conversion: x => Math.clampMin(Math.pow(10000 * x, 1.6), 1), + formatSecondaryEffect: x => format(x, 2, 2), + alteredColor: () => GlyphAlteration.getAdditionColor("dilation"), + alterationType: ALTERATION_TYPE.ADDITION + }, + dilationpow: { + id: "dilationpow", + bitmaskIndex: 7, + isGenerated: true, + glyphTypes: ["dilation"], + singleDesc: "Antimatter Dimension power +{value} while Dilated", + totalDesc: "Antimatter Dimension multipliers ^{value} while Dilated", + genericDesc: "Antimatter Dimensions ^x while Dilated", + shortDesc: "Dilated AD power +{value}", + effect: (level, strength) => 1.1 + Math.pow(level, 0.7) * Math.pow(strength, 0.7) / 25, + formatEffect: x => format(x, 2, 2), + formatSingleEffect: x => format(x - 1, 2, 2), + combine: GlyphCombiner.addExponents, + enabledInDoomed: true, + }, + replicationspeed: { + id: "replicationspeed", + bitmaskIndex: 8, + isGenerated: true, + glyphTypes: ["replication"], + singleDesc: "Multiply Replication speed by {value}", + totalDesc: "Replication speed ×{value}", + genericDesc: "Replication speed multiplier", + shortDesc: "Replication speed ×{value}", + effect: (level, strength) => (GlyphAlteration.isEmpowered("replication") + ? DC.D1_007.pow(level).times(10) + : Decimal.times(level, strength).times(3)), + formatEffect: x => format(x, 2, 1), + combine: GlyphCombiner.multiplyDecimal, + alteredColor: () => GlyphAlteration.getEmpowermentColor("replication"), + alterationType: ALTERATION_TYPE.EMPOWER + }, + replicationpow: { + id: "replicationpow", + bitmaskIndex: 9, + isGenerated: true, + glyphTypes: ["replication"], + singleDesc: "Replicanti multiplier power +{value}", + totalDesc: "Replicanti multiplier ^{value}", + shortDesc: "Replicanti mult. power +{value}", + effect: (level, strength) => 1.1 + Math.pow(level, 0.5) * strength / 25 + + GlyphAlteration.sacrificeBoost("replication") * 3, + formatEffect: x => format(x, 2, 2), + formatSingleEffect: x => format(x - 1, 2, 2), + combine: GlyphCombiner.addExponents, + alteredColor: () => GlyphAlteration.getBoostColor("replication"), + alterationType: ALTERATION_TYPE.BOOST, + enabledInDoomed: true, + }, + replicationdtgain: { + id: "replicationdtgain", + bitmaskIndex: 10, + isGenerated: true, + glyphTypes: ["replication"], + singleDesc: () => (GlyphAlteration.isAdded("replication") + ? "Multiply Dilated Time \n[and Replicanti speed] by \nlog₁₀(replicanti)×{value}" + : "Multiply Dilated Time gain by \nlog₁₀(replicanti)×{value}"), + totalDesc: () => (GlyphAlteration.isAdded("replication") + ? "Dilated Time gain and Replication speed ×(log₁₀(replicanti)×{value})" + : "Dilated Time gain ×(log₁₀(replicanti)×{value})"), + genericDesc: () => (GlyphAlteration.isAdded("replication") + ? "Dilated Time+Replicanti mult (log₁₀(replicanti))" + : "Dilated Time gain multiplier (log₁₀(replicanti))"), + shortDesc: () => (GlyphAlteration.isAdded("replication") + ? "DT and repl. ×log₁₀(repl.)×{value}" + : "DT ×log₁₀(repl.)×{value}"), + effect: (level, strength) => 0.0003 * Math.pow(level, 0.3) * Math.pow(strength, 0.65), + formatEffect: x => format(x, 5, 5), + formatSingleEffect: x => format(x, 5, 5), + // It's bad to stack this one additively (N glyphs acts as a DT mult of N) or multiplicatively (the raw number is + // less than 1), so instead we do a multiplicative stacking relative to the "base" effect of a level 1, 0% glyph. + // We also introduce a 3x mult per glyph after the first, so that stacking level 1, 0% glyphs still has an effect. + // This is still just a flat DT mult when stacking multiple glyphs, but at least it's bigger than 2 or 3. + combine: effects => ({ + value: effects.length === 0 ? 0 : effects.reduce(Number.prodReducer, Math.pow(0.0001, 1 - effects.length)), + capped: false + }), + conversion: x => x, + formatSecondaryEffect: x => format(x, 2, 3), + formatSingleSecondaryEffect: x => format(x, 5, 5), + alteredColor: () => GlyphAlteration.getAdditionColor("replication"), + alterationType: ALTERATION_TYPE.ADDITION + }, + replicationglyphlevel: { + id: "replicationglyphlevel", + bitmaskIndex: 11, + isGenerated: true, + glyphTypes: ["replication"], + singleDesc: () => `Replicanti factor for Glyph level:\n ^${format(0.4, 1, 1)} + ➜ ^(${format(0.4, 1, 1)} + {value})`, + totalDesc: () => `Replicanti factor for Glyph level: ^${format(0.4, 1, 1)} + ➜ ^(${format(0.4, 1, 1)} + {value})`, + genericDesc: "Replicanti factor for Glyph level", + shortDesc: "Replicanti pow. for level +{value}", + effect: (level, strength) => Math.pow(Math.pow(level, 0.25) * Math.pow(strength, 0.4), 0.5) / 50, + formatEffect: x => format(x, 3, 3), + combine: effects => { + let sum = effects.reduce(Number.sumReducer, 0); + if (effects.length > 2) sum *= 6 / (effects.length + 4); + return sum > 0.1 + ? { value: 0.1 + 0.2 * (sum - 0.1), capped: true } + : { value: sum, capped: effects.length > 2 }; + } + }, + infinitypow: { + id: "infinitypow", + bitmaskIndex: 12, + isGenerated: true, + glyphTypes: ["infinity"], + singleDesc: "Infinity Dimension power +{value}", + totalDesc: "Infinity Dimension multipliers ^{value}", + shortDesc: "ID power +{value}", + effect: (level, strength) => 1.007 + Math.pow(level, 0.21) * Math.pow(strength, 0.4) / 75 + + GlyphAlteration.sacrificeBoost("infinity") / 50, + formatEffect: x => format(x, 3, 3), + formatSingleEffect: x => format(x - 1, 3, 3), + combine: GlyphCombiner.addExponents, + alteredColor: () => GlyphAlteration.getBoostColor("infinity"), + alterationType: ALTERATION_TYPE.BOOST, + enabledInDoomed: true, + }, + infinityrate: { + id: "infinityrate", + bitmaskIndex: 13, + isGenerated: true, + glyphTypes: ["infinity"], + singleDesc: () => `Infinity Power conversion rate: \n^${formatInt(7)} + ➜ ^(${formatInt(7)} + {value})`, + totalDesc: () => `Infinity Power conversion rate: ^${formatInt(7)} + ➜ ^(${formatInt(7)} + {value})`, + genericDesc: "Infinity Power conversion rate", + shortDesc: "Infinity Power conversion +{value}", + effect: (level, strength) => Math.pow(level, 0.2) * Math.pow(strength, 0.4) * 0.04, + formatEffect: x => format(x, 2, 2), + combine: GlyphCombiner.add, + enabledInDoomed: true, + }, + infinityIP: { + id: "infinityIP", + bitmaskIndex: 14, + isGenerated: true, + glyphTypes: ["infinity"], + singleDesc: () => (GlyphAlteration.isAdded("infinity") + ? "Infinity Point gain \n×{value} [and ^]{value2}" + : "Multiply Infinity Point gain by {value}"), + totalDesc: () => (GlyphAlteration.isAdded("infinity") + ? "Infinity Point gain ×{value} and ^{value2}" + : "Infinity Point gain ×{value}"), + genericDesc: () => (GlyphAlteration.isAdded("infinity") + ? "Infinity Point gain multiplier and power" + : "Infinity Point gain multiplier"), + shortDesc: () => (GlyphAlteration.isAdded("infinity") + ? "IP ×{value} and ^{value2}" + : "IP ×{value}"), + effect: (level, strength) => Math.pow(level * (strength + 1), 6) * 10000, + formatEffect: x => format(x, 2, 3), + combine: GlyphCombiner.multiply, + // eslint-disable-next-line no-negated-condition + softcap: value => ((Effarig.eternityCap !== undefined) ? Math.min(value, Effarig.eternityCap.toNumber()) : value), + conversion: x => 1 + Math.log10(x) / 1800, + formatSecondaryEffect: x => format(x, 4, 4), + alteredColor: () => GlyphAlteration.getAdditionColor("infinity"), + alterationType: ALTERATION_TYPE.ADDITION + }, + infinityinfmult: { + id: "infinityinfmult", + bitmaskIndex: 15, + isGenerated: true, + glyphTypes: ["infinity"], + singleDesc: "Multiply Infinity gain by {value}", + totalDesc: "Infinity gain ×{value}", + genericDesc: "Infinity gain multiplier", + shortDesc: "Infinities ×{value}", + effect: (level, strength) => (GlyphAlteration.isEmpowered("infinity") + ? DC.D1_02.pow(level) + : Decimal.pow(level * strength, 1.5).times(2)), + formatEffect: x => format(x, 2, 1), + combine: GlyphCombiner.multiplyDecimal, + alteredColor: () => GlyphAlteration.getEmpowermentColor("infinity"), + alterationType: ALTERATION_TYPE.EMPOWER + }, + powerpow: { + id: "powerpow", + bitmaskIndex: 16, + isGenerated: true, + glyphTypes: ["power"], + singleDesc: () => (GlyphAlteration.isAdded("power") + ? "Antimatter Dimension power +{value}\n[and Antimatter Galaxy cost ×]{value2}" + : "Antimatter Dimension power +{value}"), + totalDesc: () => (GlyphAlteration.isAdded("power") + ? "Antimatter Dimension multipliers ^{value} and Antimatter Galaxy cost ×{value2}" + : "Antimatter Dimension multipliers ^{value}"), + genericDesc: () => (GlyphAlteration.isAdded("power") + ? "Antimatter Dimensions multipliers ^x and Antimatter Galaxy cost multiplier" + : "Antimatter Dimension multipliers ^x"), + shortDesc: () => (GlyphAlteration.isAdded("power") + ? "AD power +{value} and AG cost ×{value2}" + : "AD power +{value}"), + effect: (level, strength) => 1.015 + Math.pow(level, 0.2) * Math.pow(strength, 0.4) / 75, + formatEffect: x => format(x, 3, 3), + formatSingleEffect: x => format(x - 1, 3, 3), + combine: GlyphCombiner.addExponents, + conversion: x => 2 / (x + 1), + formatSecondaryEffect: x => format(x, 3, 3), + alteredColor: () => GlyphAlteration.getAdditionColor("power"), + alterationType: ALTERATION_TYPE.ADDITION, + enabledInDoomed: true, + }, + powermult: { + id: "powermult", + bitmaskIndex: 17, + isGenerated: true, + glyphTypes: ["power"], + singleDesc: "Antimatter Dimension multipliers ×{value}", + shortDesc: "AD ×{value}", + effect: (level, strength) => (GlyphAlteration.isEmpowered("power") + ? DC.D11111.pow(level * 220) + : Decimal.pow(level * strength * 10, level * strength * 10)), + formatEffect: x => formatPostBreak(x, 2, 0), + combine: GlyphCombiner.multiplyDecimal, + alteredColor: () => GlyphAlteration.getEmpowermentColor("power"), + alterationType: ALTERATION_TYPE.EMPOWER, + enabledInDoomed: true, + }, + powerdimboost: { + id: "powerdimboost", + bitmaskIndex: 18, + isGenerated: true, + glyphTypes: ["power"], + singleDesc: "Dimension Boost multiplier ×{value}", + genericDesc: "Dimension Boost multiplier", + shortDesc: "Dimboost mult. ×{value}", + effect: (level, strength) => Math.pow(level * strength, 0.5) * + Math.pow(1 + GlyphAlteration.sacrificeBoost("power"), 3), + formatEffect: x => format(x, 2, 2), + combine: GlyphCombiner.multiply, + alteredColor: () => GlyphAlteration.getBoostColor("power"), + alterationType: ALTERATION_TYPE.BOOST, + enabledInDoomed: true, + }, + powerbuy10: { + id: "powerbuy10", + bitmaskIndex: 19, + isGenerated: true, + glyphTypes: ["power"], + singleDesc: () => `Increase the bonus from buying ${formatInt(10)} Antimatter Dimensions by {value}`, + totalDesc: () => `Multiplier from "Buy ${formatInt(10)}" ×{value}`, + genericDesc: () => `"Buy ${formatInt(10)}" bonus increase`, + shortDesc: () => `AD "Buy ${formatInt(10)}" mult. ×{value}`, + effect: (level, strength) => 1 + level * strength / 12, + formatEffect: x => format(x, 2, 2), + combine: GlyphCombiner.addExponents, + enabledInDoomed: true, + }, + effarigblackhole: { + id: "effarigblackhole", + bitmaskIndex: 20, + isGenerated: true, + glyphTypes: ["effarig"], + singleDesc: "Game speed power +{value}", + totalDesc: "Game speed ^{value}", + genericDesc: "Game speed ^x", + shortDesc: "Game speed power +{value}", + effect: (level, strength) => 1 + Math.pow(level, 0.25) * Math.pow(strength, 0.4) / 75, + formatEffect: x => format(x, 3, 3), + formatSingleEffect: x => format(x - 1, 3, 3), + combine: GlyphCombiner.addExponents, + }, + effarigrm: { + id: "effarigrm", + bitmaskIndex: 21, + isGenerated: true, + glyphTypes: ["effarig"], + singleDesc: "Reality Machine multiplier ×{value}", + genericDesc: "Reality Machine multiplier", + shortDesc: "RM ×{value}", + effect: (level, strength) => (GlyphAlteration.isEmpowered("effarig") + ? Math.pow(level, 1.5) + : Math.pow(level, 0.6) * strength), + formatEffect: x => format(x, 2, 2), + combine: GlyphCombiner.multiply, + alteredColor: () => GlyphAlteration.getEmpowermentColor("effarig"), + alterationType: ALTERATION_TYPE.EMPOWER + }, + effarigglyph: { + id: "effarigglyph", + bitmaskIndex: 22, + isGenerated: true, + glyphTypes: ["effarig"], + singleDesc: "Glyph Instability starting level +{value}", + genericDesc: "Glyph Instability delay", + shortDesc: "Instability delay +{value}", + effect: (level, strength) => Math.floor(10 * Math.pow(level * strength, 0.5)), + formatEffect: x => formatInt(x), + combine: GlyphCombiner.add, + }, + effarigachievement: { + id: "effarigachievement", + bitmaskIndex: 23, + isGenerated: true, + glyphTypes: ["effarig"], + singleDesc: "Achievement multiplier power +{value}", + totalDesc: "Achievement multiplier ^{value}", + genericDesc: "Achievement multiplier ^x", + shortDesc: "Achievement mult. power +{value}", + effect: (level, strength) => 1 + Math.pow(level, 0.4) * Math.pow(strength, 0.6) / 60 + + GlyphAlteration.sacrificeBoost("effarig") / 10, + formatEffect: x => format(x, 3, 3), + formatSingleEffect: x => format(x - 1, 3, 3), + combine: GlyphCombiner.addExponents, + alteredColor: () => GlyphAlteration.getBoostColor("effarig"), + alterationType: ALTERATION_TYPE.BOOST + }, + effarigforgotten: { + id: "effarigforgotten", + bitmaskIndex: 24, + isGenerated: true, + glyphTypes: ["effarig"], + singleDesc: () => (GlyphAlteration.isAdded("effarig") + ? `"Buy ${formatInt(10)}" multiplier ^{value} [and\nDimension Boost multiplier ^]{value2}` + : `Bonus from buying ${formatInt(10)} Dimensions ^{value}`), + totalDesc: () => (GlyphAlteration.isAdded("effarig") + ? `Multiplier from "Buy ${formatInt(10)}" ^{value} and Dimension Boost multiplier ^{value2}` + : `Multiplier from "Buy ${formatInt(10)}" ^{value}`), + genericDesc: () => (GlyphAlteration.isAdded("effarig") + ? `"Buy ${formatInt(10)}" and Dimension Boost multipliers ^x` + : `"Buy ${formatInt(10)}" multiplier ^x`), + shortDesc: () => (GlyphAlteration.isAdded("effarig") + ? `Buy ${formatInt(10)} mult. ^{value}, Dimboost mult. ^{value2}` + : `Buy ${formatInt(10)} mult. ^{value}`), + effect: (level, strength) => 1 + 2 * Math.pow(level, 0.25) * Math.pow(strength, 0.4), + formatEffect: x => format(x, 2, 2), + combine: GlyphCombiner.multiply, + conversion: x => Math.pow(x, 0.4), + formatSecondaryEffect: x => format(x, 2, 2), + alteredColor: () => GlyphAlteration.getAdditionColor("effarig"), + alterationType: ALTERATION_TYPE.ADDITION + }, + effarigdimensions: { + id: "effarigdimensions", + bitmaskIndex: 25, + isGenerated: true, + glyphTypes: ["effarig"], + singleDesc: "All Dimension power +{value}", + totalDesc: "All Dimension multipliers ^{value}", + genericDesc: "All Dimension multipliers ^x", + shortDesc: "All Dimension power +{value}", + effect: (level, strength) => 1 + Math.pow(level, 0.25) * Math.pow(strength, 0.4) / 500, + formatEffect: x => format(x, 3, 3), + formatSingleEffect: x => format(x - 1, 3, 3), + combine: GlyphCombiner.addExponents, + }, + effarigantimatter: { + id: "effarigantimatter", + bitmaskIndex: 26, + isGenerated: true, + glyphTypes: ["effarig"], + singleDesc: () => `Antimatter production:\n${formatInt(10)}^x ➜ ${formatInt(10)}^(x^{value})`, + genericDesc: "Antimatter production exponent power", + shortDesc: "AM production exponent ^{value}", + effect: (level, strength) => 1 + Math.pow(level, 0.25) * Math.pow(strength, 0.4) / 5000, + formatEffect: x => format(x, 4, 4), + combine: GlyphCombiner.multiply, + }, + timeshardpow: { + id: "timeshardpow", + bitmaskIndex: 27, + isGenerated: true, + // This gets explicitly added to time glyphs elsewhere (once unlocked) + glyphTypes: [], + singleDesc: "Time Shard power +{value}", + totalDesc: "Time Shard gain ^{value}", + genericDesc: "Time Shards ^x", + shortDesc: "Time Shard power +{value}", + effect: (level, strength) => 1 + (strength / 3.5) * Math.pow(level, 0.35) / 400, + formatEffect: x => format(x, 3, 3), + formatSingleEffect: x => format(x - 1, 3, 3), + combine: GlyphCombiner.addExponents, + enabledInDoomed: true, + }, + cursedgalaxies: { + id: "cursedgalaxies", + bitmaskIndex: 0, + isGenerated: false, + glyphTypes: ["cursed"], + singleDesc: `All Galaxies are {value} weaker`, + totalDesc: "All Galaxy strength -{value}", + shortDesc: "Galaxy Strength -{value}", + // Multiplies by 0.768 per glyph + effect: level => Math.pow(level, -0.03), + formatEffect: x => formatPercents(1 - x, 2), + combine: GlyphCombiner.multiply, + }, + curseddimensions: { + id: "curseddimensions", + bitmaskIndex: 1, + isGenerated: false, + glyphTypes: ["cursed"], + singleDesc: "All Dimension multipliers ^{value}", + shortDesc: "All Dimensions ^{value}", + // Multiplies by 0.734 per glyph + effect: level => Math.pow(level, -0.035), + formatEffect: x => format(x, 3, 3), + combine: GlyphCombiner.multiply, + }, + cursedtickspeed: { + id: "cursedtickspeed", + bitmaskIndex: 2, + isGenerated: false, + glyphTypes: ["cursed"], + singleDesc: "The threshold for Tickspeed Upgrades from Time Dimensions is multiplied by ×{value}", + totalDesc: "The threshold for Tickspeed Upgrades from Time Dimensions is increased by ×{value}", + shortDesc: "TD Tickspeed threshold ×{value}", + // Additive 3.82 per glyph + effect: level => Math.clampMin(Math.log10(level), 1), + formatEffect: x => format(x, 3, 3), + combine: GlyphCombiner.add, + }, + cursedEP: { + id: "cursedEP", + bitmaskIndex: 3, + isGenerated: false, + glyphTypes: ["cursed"], + singleDesc: "Divide Eternity Point gain by {value}", + totalDesc: "Eternity Point gain / {value}", + shortDesc: "EP / {value}", + // Divides e666.6 per glyph + effect: level => Decimal.pow10(-level / 10), + formatEffect: x => format(x.reciprocal()), + combine: GlyphCombiner.multiplyDecimal, + }, + realityglyphlevel: { + id: "realityglyphlevel", + bitmaskIndex: 4, + isGenerated: false, + glyphTypes: ["reality"], + singleDesc: "Increase the effective level of equipped basic Glyphs by {value}", + totalDesc: "Equipped basic Glyph level +{value}", + shortDesc: "Basic Glyph Level +{value}", + effect: level => Math.floor(Math.sqrt(level * 90)), + formatEffect: x => formatInt(x), + combine: GlyphCombiner.add, + }, + realitygalaxies: { + id: "realitygalaxies", + bitmaskIndex: 5, + isGenerated: false, + glyphTypes: ["reality"], + singleDesc: "All Galaxies are {value} stronger", + totalDesc: "All Galaxy strength +{value}", + shortDesc: "Galaxy Strength +{value}", + effect: level => 1 + Math.pow(level / 100000, 0.5), + formatEffect: x => formatPercents(x - 1, 2), + combine: GlyphCombiner.multiply, + }, + realityrow1pow: { + id: "realityrow1pow", + bitmaskIndex: 6, + isGenerated: false, + glyphTypes: ["reality"], + singleDesc: "Multiplier from Reality Upgrade Amplifiers ^{value}", + totalDesc: "Reality Upgrade Amplifier multiplier ^{value}", + shortDesc: "Amplifier Multiplier ^{value}", + effect: level => 1 + level / 125000, + formatEffect: x => format(x, 3, 3), + combine: GlyphCombiner.addExponents, + }, + realityDTglyph: { + id: "realityDTglyph", + bitmaskIndex: 7, + isGenerated: false, + glyphTypes: ["reality"], + singleDesc: () => `Dilated Time factor for Glyph level: \n^${format(1.3, 1, 1)} + ➜ ^(${format(1.3, 1, 1)} + {value})`, + totalDesc: () => `Dilated Time factor for Glyph level: ^${format(1.3, 1, 1)} + ➜ ^(${format(1.3, 1, 1)} + {value})`, + genericDesc: "Dilated Time factor for Glyph level", + shortDesc: "DT pow. for level +{value}", + // You can only get this effect on level 25000 reality glyphs anyway, might as well make it look nice + effect: () => 0.1, + formatEffect: x => format(x, 2, 2), + combine: GlyphCombiner.add, + }, + companiondescription: { + id: "companiondescription", + bitmaskIndex: 8, + isGenerated: false, + glyphTypes: ["companion"], + singleDesc: "It does nothing but sit there and cutely smile at you, whisper into your dreams politely, " + + "and plot the demise of all who stand against you. This one-of-a-kind Glyph will never leave you.", + totalDesc: "+{value} happiness", + shortDesc: "Doesn't want to hurt you", + effect: () => { + if (Enslaved.isRunning) return 0; + const cursedCount = Glyphs.active.countWhere(g => g?.type === "cursed"); + if (cursedCount > 0) return Math.pow(0.2 + 0.2 * Math.random(), cursedCount); + return 0.4 + 0.6 * Math.random(); + }, + formatEffect: x => formatPercents(x, 2, 2), + combine: GlyphCombiner.add, + enabledInDoomed: true, + }, + companionEP: { + id: "companionEP", + bitmaskIndex: 9, + isGenerated: false, + glyphTypes: ["companion"], + singleDesc: "Thanks for your dedication for the game! You reached {value} Eternity Points on your first Reality.", + shortDesc: "It loves you very, very much", + totalDesc: () => ((Enslaved.isRunning || Glyphs.active.countWhere(g => g?.type === "cursed")) ? "Help me" : "Yay!"), + // The EP value for this is entirely encoded in rarity, but level needs to be present to + // make sure the proper parameter is being used. The actual glyph level shouldn't do anything. + // eslint-disable-next-line no-unused-vars + effect: (level, strength) => Decimal.pow10(1e6 * strengthToRarity(strength)), + formatEffect: x => formatPostBreak(x, 2), + combine: GlyphCombiner.multiplyDecimal, + enabledInDoomed: true, + } +}; diff --git a/javascripts/core/secret-formula/reality/glyph-sacrifices.js b/javascripts/core/secret-formula/reality/glyph-sacrifices.js index edb2aaad9..4df2bf7bc 100644 --- a/javascripts/core/secret-formula/reality/glyph-sacrifices.js +++ b/javascripts/core/secret-formula/reality/glyph-sacrifices.js @@ -1,7 +1,7 @@ -import { GameDatabase } from "../game-database.js"; +import { GameDatabase } from "../game-database"; -GameDatabase.reality.glyphSacrifice = [ - { +GameDatabase.reality.glyphSacrifice = { + "power": { id: "power", effect: added => { if (Pelle.isDisabled("glyphsac")) return 0; @@ -11,15 +11,16 @@ GameDatabase.reality.glyphSacrifice = [ return Math.floor(750 * Math.pow(base, 1.2)); }, description: amount => { - if (Pelle.isDisabled("glyphsac")) return `Glyph Sacrifice is disabled in Pelle`; const sacCap = GlyphSacrificeHandler.maxSacrificeForEffects; const nextDistantGalaxy = Math.pow(10, Math.pow((amount + 1) / 750, 1 / 1.2) * Math.log10(sacCap)) - 1; const nextGalaxyText = amount < 750 ? ` (next at ${format(nextDistantGalaxy, 2, 2)})` : ""; return `Distant Galaxy scaling starts ${formatInt(amount)} later${nextGalaxyText}`; - } - }, { + }, + cap: () => GlyphSacrificeHandler.maxSacrificeForEffects + }, + "infinity": { id: "infinity", effect: added => { if (Pelle.isDisabled("glyphsac")) return 1; @@ -27,11 +28,10 @@ GameDatabase.reality.glyphSacrifice = [ const capped = Math.clampMax(sac, GlyphSacrificeHandler.maxSacrificeForEffects); return 1 + Math.log10(1 + Math.pow(capped, 0.2) / 100); }, - description: amount => { - if (Pelle.isDisabled("glyphsac")) return `Glyph Sacrifice is disabled in Pelle`; - return `${formatX(amount, 2, 2)} bigger multiplier when buying 8th Infinity Dimension.`; - } - }, { + description: amount => `${formatX(amount, 2, 2)} bigger multiplier when buying 8th Infinity Dimension`, + cap: () => GlyphSacrificeHandler.maxSacrificeForEffects + }, + "time": { id: "time", effect: added => { if (Pelle.isDisabled("glyphsac")) return 1; @@ -39,11 +39,10 @@ GameDatabase.reality.glyphSacrifice = [ const capped = Math.clampMax(sac, GlyphSacrificeHandler.maxSacrificeForEffects); return Math.pow(1 + Math.pow(capped, 0.2) / 100, 2); }, - description: amount => { - if (Pelle.isDisabled("glyphsac")) return `Glyph Sacrifice is disabled in Pelle`; - return `${formatX(amount, 2, 2)} bigger multiplier when buying 8th Time Dimension.`; - } - }, { + description: amount => `${formatX(amount, 2, 2)} bigger multiplier when buying 8th Time Dimension`, + cap: () => GlyphSacrificeHandler.maxSacrificeForEffects + }, + "replication": { id: "replication", effect: added => { if (Pelle.isDisabled("glyphsac")) return 0; @@ -53,15 +52,16 @@ GameDatabase.reality.glyphSacrifice = [ return Math.floor(1500 * Math.pow(base, 1.2)); }, description: amount => { - if (Pelle.isDisabled("glyphsac")) return `Glyph Sacrifice is disabled in Pelle`; const sacCap = GlyphSacrificeHandler.maxSacrificeForEffects; const nextDistantGalaxy = Math.pow(10, Math.pow((amount + 1) / 1500, 1 / 1.2) * Math.log10(sacCap)) - 1; const nextGalaxyText = amount < 1500 ? ` (next at ${format(nextDistantGalaxy, 2, 2)})` : ""; return `Replicanti Galaxy scaling starts ${formatInt(amount)} later${nextGalaxyText}`; - } - }, { + }, + cap: () => GlyphSacrificeHandler.maxSacrificeForEffects + }, + "dilation": { id: "dilation", effect: added => { if (Pelle.isDisabled("glyphsac")) return 1; @@ -71,32 +71,29 @@ GameDatabase.reality.glyphSacrifice = [ Math.log10(GlyphSacrificeHandler.maxSacrificeForEffects), 0.1); return Math.pow(Math.clampMin(capped, 1), exponent); }, - description: amount => { - if (Pelle.isDisabled("glyphsac")) return `Glyph Sacrifice is disabled in Pelle`; - return `Multiply Tachyon Particle gain by ${formatX(amount, 2, 2)}`; - } - }, { + description: amount => `Multiply Tachyon Particle gain by ${formatX(amount, 2, 2)}`, + cap: () => GlyphSacrificeHandler.maxSacrificeForEffects + }, + "effarig": { id: "effarig", effect: added => { if (Pelle.isDisabled("glyphsac")) return 0; const sac = player.reality.glyphs.sac.effarig + (added ?? 0); - const capped = Math.clampMax(sac, GlyphSacrificeHandler.maxSacrificeForEffects); + // This doesn't use the GlyphSacrificeHandler cap because it hits its cap (+100%) earlier + const capped = Math.clampMax(sac, 1e70); return 2 * Math.log10(capped / 1e20 + 1); }, - description: amount => { - if (Pelle.isDisabled("glyphsac")) return `Glyph Sacrifice is disabled in Pelle`; - return `+${formatPercents(amount / 100, 2)} additional Glyph rarity`; - } - }, { + description: amount => `+${formatPercents(amount / 100, 2)} additional Glyph rarity`, + cap: () => 1e70 + }, + "reality": { id: "reality", effect: added => { if (Pelle.isDisabled("glyphsac")) return 0; const sac = player.reality.glyphs.sac.reality + (added ?? 0); - return 1 + Math.sqrt(sac) / 25; + return 1 + Math.sqrt(sac) / 15; }, - description: amount => { - if (Pelle.isDisabled("glyphsac")) return `Glyph Sacrifice is disabled in Pelle`; - return `${formatPercents(amount - 1, 2)} increased Alchemy yield`; - } + description: amount => `Multiply Memory Chunk gain by ${formatX(amount, 2, 3)}`, + cap: () => GlyphSacrificeHandler.maxSacrificeForEffects } -].mapToObject(g => g.id, g => g); +}; diff --git a/javascripts/core/secret-formula/reality/glyph-types.js b/javascripts/core/secret-formula/reality/glyph-types.js new file mode 100644 index 000000000..4e6db5189 --- /dev/null +++ b/javascripts/core/secret-formula/reality/glyph-types.js @@ -0,0 +1,71 @@ +import { GameDatabase } from "../game-database"; + +GameDatabase.reality.glyphTypes = { + time: { + id: "time", + symbol: GLYPH_SYMBOLS.time, + color: "#b241e3", + primaryEffect: "timepow", + alchemyResource: ALCHEMY_RESOURCE.TIME, + hasRarity: true + }, + dilation: { + id: "dilation", + symbol: GLYPH_SYMBOLS.dilation, + color: "#64dd17", + alchemyResource: ALCHEMY_RESOURCE.DILATION, + hasRarity: true + }, + replication: { + id: "replication", + symbol: GLYPH_SYMBOLS.replication, + color: "#03a9f4", + alchemyResource: ALCHEMY_RESOURCE.REPLICATION, + hasRarity: true + }, + infinity: { + id: "infinity", + symbol: GLYPH_SYMBOLS.infinity, + color: "#b67f33", + primaryEffect: "infinitypow", + alchemyResource: ALCHEMY_RESOURCE.INFINITY, + hasRarity: true + }, + power: { + id: "power", + symbol: GLYPH_SYMBOLS.power, + color: "#22aa48", + primaryEffect: "powerpow", + alchemyResource: ALCHEMY_RESOURCE.POWER, + hasRarity: true + }, + effarig: { + id: "effarig", + symbol: GLYPH_SYMBOLS.effarig, + color: "#e21717", + isUnlocked: () => EffarigUnlock.reality.isUnlocked, + alchemyResource: ALCHEMY_RESOURCE.EFFARIG, + hasRarity: true + // Effarig glyphs have no primary effect; all are equally likely + }, + reality: { + id: "reality", + symbol: GLYPH_SYMBOLS.reality, + color: "#555555", + isUnlocked: () => false, + // Refining a reality glyph is pretty wasteful anyway, but might as well have this here + alchemyResource: ALCHEMY_RESOURCE.REALITY + }, + cursed: { + id: "cursed", + symbol: GLYPH_SYMBOLS.cursed, + color: "black", + isUnlocked: () => false, + }, + companion: { + id: "companion", + symbol: GLYPH_SYMBOLS.companion, + color: "#feaec9", + isUnlocked: () => false, + }, +}; diff --git a/javascripts/core/secret-formula/reality/imaginary-upgrades.js b/javascripts/core/secret-formula/reality/imaginary-upgrades.js index 8b33e189d..767fcf13b 100644 --- a/javascripts/core/secret-formula/reality/imaginary-upgrades.js +++ b/javascripts/core/secret-formula/reality/imaginary-upgrades.js @@ -1,293 +1,303 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; -GameDatabase.reality.imaginaryUpgrades = (function() { - const rebuyable = props => { - props.cost = () => props.initialCost * Math.pow(props.costMult, player.reality.imaginaryRebuyables[props.id]); - const { effect } = props; - if (props.isDecimal) props.effect = () => Decimal.pow(effect, player.reality.imaginaryRebuyables[props.id]); - else props.effect = () => effect * player.reality.imaginaryRebuyables[props.id]; - if (!props.formatEffect) props.formatEffect = value => `+${format(value, 2, 2)}`; - props.formatCost = value => format(value, 2, 0); - return props; - }; - return [ - rebuyable({ - name: "Temporal Intensifier", - id: 1, - initialCost: 3, - costMult: 60, - description: () => `Increase Temporal Amplifier multiplier by +${format(0.15, 2, 2)}`, - effect: 0.15 - }), - rebuyable({ - name: "Replicative Intensifier", - id: 2, - initialCost: 4, - costMult: 60, - description: () => `Increase Replicative Amplifier multiplier by +${format(0.15, 2, 2)}`, - effect: 0.15 - }), - rebuyable({ - name: "Eternal Intensifier", - id: 3, - initialCost: 1, - costMult: 40, - description: () => `Increase Eternal Amplifier multiplier by +${format(0.4, 2, 2)}`, - effect: 0.4 - }), - rebuyable({ - name: "Superluminal Intensifier", - id: 4, - initialCost: 5, - costMult: 80, - description: () => `Increase Superluminal Amplifier multiplier by +${format(0.15, 2, 2)}`, - effect: 0.15 - }), - rebuyable({ - name: "Boundless Intensifier", - id: 5, - initialCost: 1, - costMult: 30, - description: () => `Increase Boundless Amplifier multiplier by +${format(0.6, 2, 2)}`, - effect: 0.6 - }), - rebuyable({ - name: "Elliptic Materiality", - id: 6, - initialCost: 1e4, - costMult: 500, - description: () => `Increase the Reality Machine cap by ${formatX(1e100)}`, - effect: 1e100, - formatEffect: value => `${formatX(value)}`, - isDecimal: true - }), - rebuyable({ - name: "Runic Assurance", - id: 7, - initialCost: 2e5, - costMult: 500, - description: () => `Delay Glyph Instability starting level by ${formatInt(200)}`, - effect: 200, - formatEffect: value => `+${formatInt(value)} levels` - }), - rebuyable({ - name: "Hyperbolic Apeirogon", - id: 8, - initialCost: 1e7, - costMult: 800, - description: () => `Multiply Infinity Dimensions by ${format("1e100000")}`, - effect: DC.E100000, - formatEffect: value => `${formatX(value)}`, - isDecimal: true - }), - rebuyable({ - name: "Cosmic Filament", - id: 9, - initialCost: 1e9, - costMult: 1000, - description: () => `Increase Galaxy strength`, - effect: 0.03, - formatEffect: value => `+${formatPercents(value)}` - }), - rebuyable({ - name: "Entropic Condensing", - id: 10, - initialCost: 8e9, - costMult: 2000, - description: () => `Increase Singularity gain`, - effect: 1, - formatEffect: value => `${formatX(1 + value, 2)}`, - }), - { - name: "Suspicion of Interference", - id: 11, - cost: 5e7, - requirement: () => `${format(1e90)} total Relic Shards - (You have ${format(player.celestials.effarig.relicShards, 2)})`, - hasFailed: () => false, - checkRequirement: () => player.celestials.effarig.relicShards >= 1e90, - checkEvent: GAME_EVENT.REALITY_RESET_AFTER, - description: "Time Dimension power based on total antimatter", - effect: () => 1 + Math.log10(player.records.totalAntimatter.log10()) / 100, - formatEffect: value => `${formatPow(value, 0, 4)}`, - }, - { - name: "Consequences of Illusions", - id: 12, - cost: 5e7, - requirement: () => `Make a level ${formatInt(9000)} Glyph with a single Glyph level factor weight at - ${formatInt(100)}`, - hasFailed: () => false, - checkRequirement: () => Object.values(player.celestials.effarig.glyphWeights).some(w => w === 100) && - gainedGlyphLevel().actualLevel >= 9000, - checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, - description: "Gain free Dimboosts based on Imaginary rebuyable count", - effect: () => 2e4 * ImaginaryUpgrades.totalRebuyables, - formatEffect: value => `${format(value, 1)}`, - }, - { - name: "Transience of Information", - id: 13, - cost: 5e7, - requirement: () => `Reach ${format(Number.MAX_VALUE, 2)} projected Reality Machines within - The Enslaved Ones' Reality`, - hasFailed: () => !Enslaved.isRunning, - checkRequirement: () => Enslaved.isRunning && MachineHandler.uncappedRM.gte(Number.MAX_VALUE), - checkEvent: GAME_EVENT.GAME_TICK_AFTER, - description: "Increase Imaginary Machine Cap based on Imaginary Upgrades purchased", - effect: () => 1 + ImaginaryUpgrades.totalRebuyables / 20 + ImaginaryUpgrades.totalSinglePurchase / 2, - formatEffect: value => `${formatX(value, 2, 1)}`, - }, - { - name: "Recollection of Intrusion", - id: 14, - cost: 3.5e8, - requirement: () => `Reach a tickspeed of ${format("1e75000000000")} / sec within Eternity Challenge 5`, - hasFailed: () => false, - checkRequirement: () => EternityChallenge(5).isRunning && Tickspeed.perSecond.exponent >= 7.5e10, - checkEvent: GAME_EVENT.GAME_TICK_AFTER, - description: () => `Raise all Dimension per-purchase multipliers to ${formatPow(1.5, 0, 1)}`, - effect: 1.5 - }, - { - name: "Fabrication of Ideals", - id: 15, - cost: 1e9, - requirement: () => `Reach ${format("1e1500000000000")} antimatter without - ever having any 1st Infinity Dimensions`, - hasFailed: () => player.requirementChecks.reality.maxID1.gt(0), - checkRequirement: () => player.requirementChecks.reality.maxID1.eq(0) && player.antimatter.exponent >= 1.5e12, - checkEvent: GAME_EVENT.GAME_TICK_AFTER, - description: "Convert Antimatter Dimensions to Continuum and unlock Lai'tela, Celestial of Dimensions", - }, - { - name: "Massless Momentum", - id: 16, - cost: 3.5e9, - formatCost: x => format(x, 1), - requirement: () => `Destabilize Lai'tela's Reality in under ${formatInt(30)} seconds twice`, - hasFailed: () => false, - checkRequirement: () => Laitela.maxAllowedDimension <= 6, - checkEvent: GAME_EVENT.GAME_TICK_AFTER, - description: "Unlock the 2nd Dark Matter Dimension", - }, - { - name: "Chiral Oscillation", - id: 17, - cost: 6e9, - requirement: () => `Automatically condense at least ${formatInt(20)} Singularities at once`, - hasFailed: () => false, - checkRequirement: () => Singularity.singularitiesGained >= 20 && - Currency.darkEnergy.gte(Singularity.cap * SingularityMilestone.autoCondense.effectValue), - checkEvent: GAME_EVENT.SINGULARITY_RESET_BEFORE, - description: "Unlock the 3rd Dark Matter Dimension", - }, - { - name: "Dimensional Symmetry", - id: 18, - cost: 1.5e10, - formatCost: x => format(x, 1), - requirement: () => `Have ${formatInt(80000)} total Galaxies`, - hasFailed: () => false, - checkRequirement: () => Replicanti.galaxies.total + player.galaxies + - player.dilation.totalTachyonGalaxies >= 80000, - checkEvent: GAME_EVENT.GAME_TICK_AFTER, - description: "Unlock the 4th Dark Matter Dimension", - }, - { - name: "Deterministic Radiation", - id: 19, - cost: 2.8e10, - formatCost: x => format(x, 1), - requirement: () => `Reach ${formatInt(3.85e6)} Tickspeed Continuum without ever having more than - ${formatInt(8)} Time Studies in this Reality`, - hasFailed: () => player.requirementChecks.reality.maxStudies > 8, - checkRequirement: () => player.requirementChecks.reality.maxStudies <= 8 && - Tickspeed.continuumValue >= 3.85e6, - checkEvent: GAME_EVENT.GAME_TICK_AFTER, - description: "Unlock Dark Matter Annihilation", - }, - { - name: "Vacuum Acceleration", - id: 20, - cost: 3e12, - requirement: () => `Have a Continuum increase of at least ${formatPercents(1)}`, - hasFailed: () => false, - checkRequirement: () => Laitela.matterExtraPurchaseFactor >= 2, - checkEvent: GAME_EVENT.GAME_TICK_AFTER, - description: () => `Unlock Autobuyers for repeatable Imaginary Upgrades and generate Imaginary Machines - ${formatInt(10)} times faster`, - effect: 10, - }, - { - name: "Existential Elimination", - id: 21, - cost: 1e13, - requirement: () => `Reach ${format("1e7400000000000")} antimatter with Continuum disabled`, - hasFailed: () => !player.requirementChecks.reality.noContinuum, - checkRequirement: () => player.requirementChecks.reality.noContinuum && - Currency.antimatter.value.log10() >= 7.4e12, - checkEvent: GAME_EVENT.GAME_TICK_AFTER, - description: "Annihilation multiplier gain is improved based on Imaginary Machines", - effect: () => Math.clampMin(Math.pow(Math.log10(Currency.imaginaryMachines.value) - 10, 3), 1), - formatEffect: value => `${formatX(value, 2, 1)}`, - }, - { - name: "Total Termination", - id: 22, - cost: 1.5e14, - formatCost: x => format(x, 1), - requirement: () => `Reach ${format("1e150000000000")} antimatter in Effarig's Reality with - at least ${formatInt(4)} Cursed Glyphs equipped`, - // Note: 4 cursed glyphs is -12 glyph count, but equipping a positive glyph in the last slot is allowed - hasFailed: () => !Effarig.isRunning || player.requirementChecks.reality.maxGlyphs > -10, - checkRequirement: () => Effarig.isRunning && player.requirementChecks.reality.maxGlyphs < -10 && - Currency.antimatter.value.exponent >= 1.5e11, - checkEvent: GAME_EVENT.GAME_TICK_AFTER, - description: () => `Glyph Sacrifice totals for basic Glyphs are increased to ${format(1e100)}`, - effect: 1e100, - }, - { - name: "Planar Purification", - id: 23, - cost: 6e14, - requirement: () => `Reach Glyph level ${formatInt(20000)} in Ra's Reality with - at most ${formatInt(0)} Glyphs equipped`, - hasFailed: () => !Ra.isRunning || player.requirementChecks.reality.maxGlyphs > 0, - checkRequirement: () => Ra.isRunning && player.requirementChecks.reality.maxGlyphs <= 0 && - gainedGlyphLevel().actualLevel >= 20000, - checkEvent: GAME_EVENT.GAME_TICK_AFTER, - description: "Increase free Dimboost count based on Tesseract count", - effect: () => Math.floor(0.25 * Math.pow(Tesseracts.effectiveCount, 2)), - formatEffect: value => `${formatX(value)}`, - }, - { - name: "Absolute Annulment", - id: 24, - cost: 6e14, - requirement: () => `Have ${formatInt(13000)} Antimatter Galaxies in Ra's Reality - with a fully inverted Black Hole`, - hasFailed: () => !Ra.isRunning || player.requirementChecks.reality.slowestBH > 1e-300, - checkRequirement: () => Ra.isRunning && player.requirementChecks.reality.slowestBH <= 1e-300 && - player.galaxies >= 13000, - checkEvent: GAME_EVENT.GAME_TICK_AFTER, - description: "Increase free Dimboost strength based on Singularity count", - effect: () => Decimal.pow(player.celestials.laitela.singularities, 300), - formatEffect: value => `${formatX(value, 2, 1)}`, - }, - { - name: "Omnipresent Obliteration", - id: 25, - cost: 1.6e15, - formatCost: x => format(x, 1), - requirement: () => `Reach Reality in Lai'tela's Reality with all Dimensions disabled and - at least ${formatInt(4)} empty Glyph slots`, - hasFailed: () => !Laitela.isRunning || Laitela.maxAllowedDimension !== 0 || Glyphs.activeList.length > 1, - checkRequirement: () => Laitela.isRunning && Laitela.maxAllowedDimension === 0 && - Glyphs.activeList.length <= 1, - checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, - description: "Unlock Pelle, Celestial of Antimatter", - }, - ]; -}()); +const rebuyable = props => { + props.cost = () => props.initialCost * Math.pow(props.costMult, player.reality.imaginaryRebuyables[props.id]); + const { effect } = props; + if (props.isDecimal) props.effect = () => Decimal.pow(effect, player.reality.imaginaryRebuyables[props.id]); + else props.effect = () => effect * player.reality.imaginaryRebuyables[props.id]; + if (!props.formatEffect) props.formatEffect = value => `+${format(value, 2, 2)}`; + props.formatCost = value => format(value, 2, 0); + return props; +}; + +GameDatabase.reality.imaginaryUpgrades = [ + rebuyable({ + name: "Temporal Intensifier", + id: 1, + initialCost: 3, + costMult: 60, + description: () => `Increase Temporal Amplifier multiplier by +${format(0.15, 2, 2)}`, + effect: 0.15 + }), + rebuyable({ + name: "Replicative Intensifier", + id: 2, + initialCost: 4, + costMult: 60, + description: () => `Increase Replicative Amplifier multiplier by +${format(0.15, 2, 2)}`, + effect: 0.15 + }), + rebuyable({ + name: "Eternal Intensifier", + id: 3, + initialCost: 1, + costMult: 40, + description: () => `Increase Eternal Amplifier multiplier by +${format(0.4, 2, 2)}`, + effect: 0.4 + }), + rebuyable({ + name: "Superluminal Intensifier", + id: 4, + initialCost: 5, + costMult: 80, + description: () => `Increase Superluminal Amplifier multiplier by +${format(0.15, 2, 2)}`, + effect: 0.15 + }), + rebuyable({ + name: "Boundless Intensifier", + id: 5, + initialCost: 1, + costMult: 30, + description: () => `Increase Boundless Amplifier multiplier by +${format(0.6, 2, 2)}`, + effect: 0.6 + }), + rebuyable({ + name: "Elliptic Materiality", + id: 6, + initialCost: 1e4, + costMult: 500, + description: () => `Increase the Reality Machine cap by ${formatX(1e100)}`, + effect: 1e100, + formatEffect: value => `${formatX(value)}`, + isDecimal: true + }), + rebuyable({ + name: "Runic Assurance", + id: 7, + initialCost: 2e5, + costMult: 500, + description: () => `Delay Glyph Instability starting level by ${formatInt(200)}`, + effect: 200, + formatEffect: value => `+${formatInt(value)} levels` + }), + rebuyable({ + name: "Hyperbolic Apeirogon", + id: 8, + initialCost: 1e7, + costMult: 800, + description: () => `Multiply Infinity Dimensions by ${format("1e100000")}`, + effect: DC.E100000, + formatEffect: value => `${formatX(value)}`, + isDecimal: true + }), + rebuyable({ + name: "Cosmic Filament", + id: 9, + initialCost: 1e9, + costMult: 1000, + description: () => `Increase Galaxy strength`, + effect: 0.03, + formatEffect: value => `+${formatPercents(value)}`, + }), + rebuyable({ + name: "Entropic Condensing", + id: 10, + initialCost: 8e9, + costMult: 2000, + description: () => `Increase Singularity gain`, + effect: 1, + formatEffect: value => `${formatX(1 + value, 2)}` + }), + { + name: "Suspicion of Interference", + id: 11, + cost: 5e7, + requirement: () => `${format(1e90)} total Relic Shards + (You have ${format(player.celestials.effarig.relicShards, 2)})`, + hasFailed: () => false, + checkRequirement: () => player.celestials.effarig.relicShards >= 1e90, + checkEvent: GAME_EVENT.REALITY_RESET_AFTER, + description: "Time Dimension power based on total antimatter", + effect: () => 1 + Math.log10(player.records.totalAntimatter.log10()) / 100, + formatEffect: value => `${formatPow(value, 0, 4)}`, + isDisabledInDoomed: true + }, + { + name: "Consequences of Illusions", + id: 12, + cost: 5e7, + requirement: () => `Make a level ${formatInt(9000)} Glyph with a single Glyph level factor weight at + ${formatInt(100)}`, + hasFailed: () => false, + checkRequirement: () => Object.values(player.celestials.effarig.glyphWeights).some(w => w === 100) && + gainedGlyphLevel().actualLevel >= 9000, + checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, + description: "Gain free Dimboosts based on Imaginary rebuyable count", + effect: () => 2e4 * ImaginaryUpgrades.totalRebuyables, + formatEffect: value => `${format(value, 1)}`, + isDisabledInDoomed: true + }, + { + name: "Transience of Information", + id: 13, + cost: 5e7, + requirement: () => `Reach ${format(Number.MAX_VALUE, 2)} projected Reality Machines within + The Nameless Ones' Reality`, + hasFailed: () => !Enslaved.isRunning, + checkRequirement: () => Enslaved.isRunning && MachineHandler.uncappedRM.gte(Number.MAX_VALUE), + checkEvent: GAME_EVENT.GAME_TICK_AFTER, + description: "Increase Imaginary Machine Cap based on Imaginary Upgrades purchased", + effect: () => 1 + ImaginaryUpgrades.totalRebuyables / 20 + ImaginaryUpgrades.totalSinglePurchase / 2, + formatEffect: value => `${formatX(value, 2, 1)}`, + isDisabledInDoomed: true + }, + { + name: "Recollection of Intrusion", + id: 14, + cost: 3.5e8, + requirement: () => `Reach a tickspeed of ${format("1e75000000000")} / sec within Eternity Challenge 5`, + hasFailed: () => false, + checkRequirement: () => EternityChallenge(5).isRunning && Tickspeed.perSecond.exponent >= 7.5e10, + checkEvent: GAME_EVENT.GAME_TICK_AFTER, + description: () => `Raise all Dimension per-purchase multipliers to ${formatPow(1.5, 0, 1)}`, + effect: 1.5, + isDisabledInDoomed: true + }, + { + name: "Fabrication of Ideals", + id: 15, + cost: 1e9, + requirement: () => `Reach ${format("1e1500000000000")} antimatter without + ever having any 1st Infinity Dimensions`, + hasFailed: () => player.requirementChecks.reality.maxID1.gt(0), + checkRequirement: () => player.requirementChecks.reality.maxID1.eq(0) && player.antimatter.exponent >= 1.5e12, + checkEvent: GAME_EVENT.GAME_TICK_AFTER, + description: () => `${ + Pelle.isDoomed ? "Unlock" : "Convert Antimatter Dimensions to Continuum and unlock" + } Lai'tela, Celestial of Dimensions`, + }, + { + name: "Massless Momentum", + id: 16, + cost: 3.5e9, + formatCost: x => format(x, 1), + requirement: () => `Destabilize Lai'tela's Reality in under ${formatInt(30)} seconds twice`, + hasFailed: () => false, + checkRequirement: () => Laitela.maxAllowedDimension <= 6, + checkEvent: GAME_EVENT.GAME_TICK_AFTER, + description: "Unlock the 2nd Dark Matter Dimension", + }, + { + name: "Chiral Oscillation", + id: 17, + cost: 6e9, + requirement: () => `Automatically condense at least ${formatInt(20)} Singularities at once`, + hasFailed: () => false, + checkRequirement: () => Singularity.singularitiesGained >= 20 && + Currency.darkEnergy.gte(Singularity.cap * SingularityMilestone.autoCondense.effectOrDefault(Infinity)), + checkEvent: GAME_EVENT.SINGULARITY_RESET_BEFORE, + description: "Unlock the 3rd Dark Matter Dimension", + }, + { + name: "Dimensional Symmetry", + id: 18, + cost: 1.5e10, + formatCost: x => format(x, 1), + requirement: () => `Have ${formatInt(80000)} total Galaxies`, + hasFailed: () => false, + checkRequirement: () => Replicanti.galaxies.total + player.galaxies + + player.dilation.totalTachyonGalaxies >= 80000, + checkEvent: GAME_EVENT.GAME_TICK_AFTER, + description: "Unlock the 4th Dark Matter Dimension", + }, + { + name: "Deterministic Radiation", + id: 19, + cost: 2.8e10, + formatCost: x => format(x, 1), + requirement: () => `Reach ${formatInt(3.85e6)} Tickspeed Continuum without ever having more than + ${formatInt(8)} Time Studies in this Reality`, + hasFailed: () => player.requirementChecks.reality.maxStudies > 8, + checkRequirement: () => player.requirementChecks.reality.maxStudies <= 8 && + Tickspeed.continuumValue >= 3.85e6, + checkEvent: GAME_EVENT.GAME_TICK_AFTER, + description: "Unlock Dark Matter Annihilation" + }, + { + name: "Vacuum Acceleration", + id: 20, + cost: 3e12, + requirement: () => `Have a Continuum increase of at least ${formatPercents(1)}`, + hasFailed: () => false, + checkRequirement: () => Laitela.matterExtraPurchaseFactor >= 2, + checkEvent: GAME_EVENT.GAME_TICK_AFTER, + description: () => `Unlock Autobuyers for repeatable Imaginary Upgrades and generate Imaginary Machines + ${formatInt(10)} times faster`, + effect: 10, + isDisabledInDoomed: true + }, + { + name: "Existential Elimination", + id: 21, + cost: 1e13, + requirement: () => `Reach ${format("1e7400000000000")} antimatter with Continuum disabled`, + hasFailed: () => !player.requirementChecks.reality.noContinuum, + checkRequirement: () => player.requirementChecks.reality.noContinuum && + Currency.antimatter.value.log10() >= 7.4e12, + checkEvent: GAME_EVENT.GAME_TICK_AFTER, + description: "Annihilation multiplier gain is improved based on Imaginary Machines", + effect: () => Math.clampMin(Math.pow(Math.log10(Currency.imaginaryMachines.value) - 10, 3), 1), + formatEffect: value => `${formatX(value, 2, 1)}`, + isDisabledInDoomed: true + }, + { + name: "Total Termination", + id: 22, + cost: 1.5e14, + formatCost: x => format(x, 1), + requirement: () => `Reach ${format("1e150000000000")} antimatter in Effarig's Reality with + at least ${formatInt(4)} Cursed Glyphs equipped`, + // Note: 4 cursed glyphs is -12 glyph count, but equipping a positive glyph in the last slot is allowed + hasFailed: () => !Effarig.isRunning || player.requirementChecks.reality.maxGlyphs > -10, + checkRequirement: () => Effarig.isRunning && player.requirementChecks.reality.maxGlyphs < -10 && + Currency.antimatter.value.exponent >= 1.5e11, + checkEvent: GAME_EVENT.GAME_TICK_AFTER, + description: () => `Glyph Sacrifice totals for basic Glyphs are increased to ${format(1e100)}`, + effect: 1e100, + isDisabledInDoomed: true + }, + { + name: "Planar Purification", + id: 23, + cost: 6e14, + requirement: () => `Reach Glyph level ${formatInt(20000)} in Ra's Reality with + at most ${formatInt(0)} Glyphs equipped`, + hasFailed: () => !Ra.isRunning || player.requirementChecks.reality.maxGlyphs > 0, + checkRequirement: () => Ra.isRunning && player.requirementChecks.reality.maxGlyphs <= 0 && + gainedGlyphLevel().actualLevel >= 20000, + checkEvent: GAME_EVENT.GAME_TICK_AFTER, + description: "Increase free Dimboost count based on Tesseract count", + effect: () => Math.floor(0.25 * Math.pow(Tesseracts.effectiveCount, 2)), + formatEffect: value => `${formatX(value)}`, + isDisabledInDoomed: true + }, + { + name: "Absolute Annulment", + id: 24, + cost: 6e14, + requirement: () => `Have ${formatInt(13000)} Antimatter Galaxies in Ra's Reality + with a fully inverted Black Hole`, + hasFailed: () => !Ra.isRunning || player.requirementChecks.reality.slowestBH > 1e-300, + checkRequirement: () => Ra.isRunning && player.requirementChecks.reality.slowestBH <= 1e-300 && + player.galaxies >= 13000, + checkEvent: GAME_EVENT.GAME_TICK_AFTER, + description: "Increase free Dimboost strength based on Singularity count", + effect: () => Decimal.pow(player.celestials.laitela.singularities, 300), + formatEffect: value => `${formatX(value, 2, 1)}`, + isDisabledInDoomed: true + }, + { + name: "Omnipresent Obliteration", + id: 25, + cost: 1.6e15, + formatCost: x => format(x, 1), + requirement: () => `Reach Reality in Lai'tela's Reality with all Dimensions disabled and + at least ${formatInt(4)} empty Glyph slots`, + hasFailed: () => !Laitela.isRunning || Laitela.maxAllowedDimension !== 0 || Glyphs.activeList.length > 1, + checkRequirement: () => Laitela.isRunning && Laitela.maxAllowedDimension === 0 && + Glyphs.activeList.length <= 1, + checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, + description: "Unlock Pelle, Celestial of Antimatter", + }, +]; diff --git a/javascripts/core/secret-formula/reality/perks.js b/javascripts/core/secret-formula/reality/perks.js index 831642e83..dc323fe6c 100644 --- a/javascripts/core/secret-formula/reality/perks.js +++ b/javascripts/core/secret-formula/reality/perks.js @@ -1,5 +1,5 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; export const PERK_FAMILY = { ANTIMATTER: "ANTIMATTER", @@ -402,22 +402,22 @@ GameDatabase.reality.perks = { shortDescription: () => "Faster Dilation Autobuyers", defaultPosition: new Vector(490, 450) }, - ttFree: { - id: 104, - label: "TTF", - family: PERK_FAMILY.AUTOMATION, - get description() { - return `Purchasing Time Theorems no longer spends your Antimatter, Infinity Points, or Eternity Points.`; - }, - defaultPosition: new Vector(190, -410) - }, ttBuySingle: { - id: 105, + id: 104, label: "TTS", family: PERK_FAMILY.AUTOMATION, description: "Unlock a Time Theorem Autobuyer which buys single Time Theorems every tick.", automatorPoints: 5, shortDescription: () => "Single TT Autobuyer", + defaultPosition: new Vector(190, -410) + }, + ttFree: { + id: 105, + label: "TTF", + family: PERK_FAMILY.AUTOMATION, + get description() { + return `Purchasing Time Theorems no longer spends your Antimatter, Infinity Points, or Eternity Points.`; + }, defaultPosition: new Vector(255, -540) }, ttBuyMax: { @@ -515,7 +515,7 @@ GameDatabase.reality.perkConnections = (function() { [p.studyPassive, p.bypassEC1Lock], [p.autocompleteEC1, p.autocompleteEC2], [p.autocompleteEC2, p.autocompleteEC3], - [p.studyActiveEP, p.bypassEC2Lock, p.ttFree], + [p.studyActiveEP, p.bypassEC2Lock, p.ttBuySingle], [p.studyIdleEP, p.bypassEC3Lock, p.autocompleteEC1], [p.studyECRequirement, p.studyECBulk], [p.retroactiveTP1, p.bypassTGReset, p.startTP, p.retroactiveTP2], @@ -523,8 +523,8 @@ GameDatabase.reality.perkConnections = (function() { [p.retroactiveTP3, p.retroactiveTP4], [p.autobuyerDilation, p.autounlockEU2, p.autounlockDilation1, p.bypassECDilation, p.bypassTGReset], [p.autobuyerFasterID], - [p.ttFree, p.ttBuySingle], - [p.ttBuySingle, p.ttBuyMax], + [p.ttBuySingle, p.ttFree], + [p.ttFree, p.ttBuyMax], [p.achievementGroup1, p.achievementGroup2], [p.achievementGroup2, p.achievementGroup3], [p.achievementGroup3, p.achievementGroup4], diff --git a/javascripts/core/secret-formula/reality/reality-upgrades.js b/javascripts/core/secret-formula/reality/reality-upgrades.js index c6e765759..e40ee731b 100644 --- a/javascripts/core/secret-formula/reality/reality-upgrades.js +++ b/javascripts/core/secret-formula/reality/reality-upgrades.js @@ -1,345 +1,345 @@ -import { GameDatabase } from "../game-database.js"; -import { DC } from "../../constants.js"; +import { DC } from "../../constants"; +import { GameDatabase } from "../game-database"; -GameDatabase.reality.upgrades = (function() { - const rebuyable = props => { - props.cost = () => getHybridCostScaling( - player.reality.rebuyables[props.id], - 1e30, - props.initialCost, - props.costMult, - props.costMult / 10, - DC.E309, - 1e3, - props.initialCost * props.costMult - ); - const { effect } = props; - props.effect = () => Math.pow( - effect + ImaginaryUpgrade(props.id).effectOrDefault(0), - player.reality.rebuyables[props.id] * getAdjustedGlyphEffect("realityrow1pow")); - props.description = () => props.textTemplate.replace("{value}", - ImaginaryUpgrade(props.id).effectValue === 0 - ? formatInt(effect) - : format(effect + ImaginaryUpgrade(props.id).effectValue, 2, 2)); - props.formatEffect = value => formatX(value, 2, 0); - props.formatCost = value => format(value, 2, 0); - return props; - }; - return [ - rebuyable({ - name: "Temporal Amplifier", - id: 1, - initialCost: 1, - costMult: 30, - textTemplate: "You gain Dilated Time {value} times faster", - effect: 3 - }), - rebuyable({ - name: "Replicative Amplifier", - id: 2, - initialCost: 1, - costMult: 30, - textTemplate: "You gain Replicanti {value} times faster", - effect: 3 - }), - rebuyable({ - name: "Eternal Amplifier", - id: 3, - initialCost: 2, - costMult: 30, - textTemplate: "You gain {value} times more Eternities", - effect: 3 - }), - rebuyable({ - name: "Superluminal Amplifier", - id: 4, - initialCost: 2, - costMult: 30, - textTemplate: "You gain {value} times more Tachyon Particles", - effect: 3 - }), - rebuyable({ - name: "Boundless Amplifier", - id: 5, - initialCost: 3, - costMult: 50, - textTemplate: "You gain {value} times more Infinities", - effect: 5 - }), - { - name: "Cosmically Duplicate", - id: 6, - cost: 15, - requirement: "Complete your first Eternity without using Replicanti Galaxies", - // Note that while noRG resets on eternity, the reality-level check will be false after the first eternity. - // The noRG variable is eternity-level as it's also used for an achievement check - hasFailed: () => !(player.requirementChecks.eternity.noRG && player.requirementChecks.reality.noEternities), - checkRequirement: () => player.requirementChecks.eternity.noRG && player.requirementChecks.reality.noEternities, - checkEvent: GAME_EVENT.ETERNITY_RESET_BEFORE, - description: "Replicanti speed is multiplied based on Replicanti Galaxies", - effect: () => 1 + Replicanti.galaxies.total / 50, - formatEffect: value => formatX(value, 2, 2) +const rebuyable = props => { + props.cost = () => getHybridCostScaling( + player.reality.rebuyables[props.id], + 1e30, + props.initialCost, + props.costMult, + props.costMult / 10, + DC.E309, + 1e3, + props.initialCost * props.costMult + ); + const { effect } = props; + props.effect = () => Math.pow( + effect + ImaginaryUpgrade(props.id).effectOrDefault(0), + player.reality.rebuyables[props.id] * getAdjustedGlyphEffect("realityrow1pow")); + props.description = () => props.textTemplate.replace("{value}", + ImaginaryUpgrade(props.id).effectValue === 0 + ? formatInt(effect) + : format(effect + ImaginaryUpgrade(props.id).effectValue, 2, 2)); + props.formatEffect = value => formatX(value, 2, 0); + props.formatCost = value => format(value, 2, 0); + return props; +}; + + +GameDatabase.reality.upgrades = [ + rebuyable({ + name: "Temporal Amplifier", + id: 1, + initialCost: 1, + costMult: 30, + textTemplate: "You gain Dilated Time {value} times faster", + effect: 3 + }), + rebuyable({ + name: "Replicative Amplifier", + id: 2, + initialCost: 1, + costMult: 30, + textTemplate: "You gain Replicanti {value} times faster", + effect: 3 + }), + rebuyable({ + name: "Eternal Amplifier", + id: 3, + initialCost: 2, + costMult: 30, + textTemplate: "You gain {value} times more Eternities", + effect: 3 + }), + rebuyable({ + name: "Superluminal Amplifier", + id: 4, + initialCost: 2, + costMult: 30, + textTemplate: "You gain {value} times more Tachyon Particles", + effect: 3 + }), + rebuyable({ + name: "Boundless Amplifier", + id: 5, + initialCost: 3, + costMult: 50, + textTemplate: "You gain {value} times more Infinities", + effect: 5 + }), + { + name: "Cosmically Duplicate", + id: 6, + cost: 15, + requirement: "Complete your first Eternity without using Replicanti Galaxies", + // Note that while noRG resets on eternity, the reality-level check will be false after the first eternity. + // The noRG variable is eternity-level as it's also used for an achievement check + hasFailed: () => !(player.requirementChecks.eternity.noRG && player.requirementChecks.reality.noEternities), + checkRequirement: () => player.requirementChecks.eternity.noRG && player.requirementChecks.reality.noEternities, + checkEvent: GAME_EVENT.ETERNITY_RESET_BEFORE, + description: "Replicanti speed is multiplied based on Replicanti Galaxies", + effect: () => 1 + Replicanti.galaxies.total / 50, + formatEffect: value => formatX(value, 2, 2) + }, + { + name: "Innumerably Construct", + id: 7, + cost: 15, + requirement: "Complete your first Infinity with at most 1 Antimatter Galaxy", + hasFailed: () => !(player.galaxies <= 1 && player.requirementChecks.reality.noInfinities), + checkRequirement: () => player.galaxies <= 1 && player.requirementChecks.reality.noInfinities, + checkEvent: GAME_EVENT.BIG_CRUNCH_BEFORE, + description: "Infinity gain is boosted from Antimatter Galaxy count", + effect: () => 1 + player.galaxies / 30, + formatEffect: value => formatX(value, 2, 2) + }, + { + name: "Paradoxically Attain", + id: 8, + cost: 15, + requirement: "Get to Eternity without any automatic Achievements", + hasFailed: () => player.reality.gainedAutoAchievements, + checkRequirement: () => !player.reality.gainedAutoAchievements, + checkEvent: GAME_EVENT.ETERNITY_RESET_BEFORE, + description: "Tachyon Particle gain is boosted based on Achievement multiplier", + effect: () => Math.sqrt(Achievements.power), + formatEffect: value => formatX(value, 2, 2) + }, + { + name: "Linguistically Expand", + id: 9, + cost: 15, + requirement: () => `Eternity for ${format("1e4000")} Eternity Points using + only a single level ${formatInt(3)}+ Glyph.`, + hasFailed: () => { + const invalidEquippedGlyphs = Glyphs.activeList.length > 1 || + (Glyphs.activeList.length === 1 && Glyphs.activeList[0].level < 3); + const hasValidGlyphInInventory = Glyphs.inventory.countWhere(g => g && g.level >= 3) > 0; + return invalidEquippedGlyphs || (Glyphs.activeList.length === 0 && !hasValidGlyphInInventory); }, - { - name: "Innumerably Construct", - id: 7, - cost: 15, - requirement: "Complete your first Infinity with at most 1 Antimatter Galaxy", - hasFailed: () => !(player.galaxies <= 1 && player.requirementChecks.reality.noInfinities), - checkRequirement: () => player.galaxies <= 1 && player.requirementChecks.reality.noInfinities, - checkEvent: GAME_EVENT.BIG_CRUNCH_BEFORE, - description: "Infinity gain is boosted from Antimatter Galaxy count", - effect: () => 1 + player.galaxies / 30, - formatEffect: value => formatX(value, 2, 2) + checkRequirement: () => Currency.eternityPoints.exponent >= 4000 && + Glyphs.activeList.length === 1 && Glyphs.activeList[0].level >= 3, + checkEvent: GAME_EVENT.ETERNITY_RESET_AFTER, + description: "Gain another Glyph slot", + effect: () => 1 + }, + { + name: "Existentially Prolong", + id: 10, + cost: 15, + requirement: () => `Complete your first Eternity with at least ${formatPostBreak(DC.E450)} Infinity Points`, + hasFailed: () => !player.requirementChecks.reality.noEternities, + checkRequirement: () => Currency.infinityPoints.exponent >= 450 && + player.requirementChecks.reality.noEternities, + checkEvent: GAME_EVENT.ETERNITY_RESET_BEFORE, + description: () => `Start every Reality with ${formatInt(100)} Eternities (also applies to current Reality)`, + automatorPoints: 15, + shortDescription: () => `Start with ${formatInt(100)} Eternities`, + effect: () => 100 + }, + { + name: "The Boundless Flow", + id: 11, + cost: 50, + requirement: () => `${format(Currency.infinitiesBanked.value, 2)}/${format(DC.E12)} Banked Infinities`, + checkRequirement: () => Currency.infinitiesBanked.exponent >= 12, + checkEvent: [GAME_EVENT.ETERNITY_RESET_AFTER, GAME_EVENT.REALITY_FIRST_UNLOCKED], + description: "Every second, gain 10% of the Infinities you would normally gain by Infinitying", + automatorPoints: 5, + shortDescription: () => `Continuous Infinity generation`, + effect: () => gainedInfinities().times(0.1), + formatEffect: value => `${format(value)} per second` + }, + { + name: "The Knowing Existence", + id: 12, + cost: 50, + requirement: () => `Eternity for ${format(DC.E70)} Eternity Points without Eternity Challenge 1`, + hasFailed: () => EternityChallenge(1).completions !== 0, + checkRequirement: () => Currency.eternityPoints.exponent >= 70 && EternityChallenge(1).completions === 0, + checkEvent: GAME_EVENT.ETERNITY_RESET_AFTER, + description: "Eternity Point multiplier based on Reality and Time Theorem count", + effect: () => Currency.timeTheorems.value + .minus(DC.E3).clampMin(2) + .pow(Math.log2(Math.min(Currency.realities.value, 1e4))).clampMin(1), + formatEffect: value => formatX(value, 2, 2) + }, + { + name: "The Telemechanical Process", + id: 13, + cost: 50, + requirement: () => `Eternity for ${format(DC.E4000)} Eternity Points without Time Dimensions 5-8`, + hasFailed: () => !Array.range(5, 4).every(i => TimeDimension(i).amount.equals(0)), + checkRequirement: () => Currency.eternityPoints.exponent >= 4000 && + Array.range(5, 4).every(i => TimeDimension(i).amount.equals(0)), + checkEvent: GAME_EVENT.ETERNITY_RESET_AFTER, + description: () => `Unlock Time Dimension, ${formatX(5)} Eternity Point multiplier, + and improved Eternity autobuyers`, + automatorPoints: 10, + shortDescription: () => `TD and ${formatX(5)} EP Autobuyers, improved Eternity Autobuyer`, + }, + { + name: "The Eternal Flow", + id: 14, + cost: 50, + requirement: () => `${format(Currency.eternities.value, 2)}/${format(1e7)} Eternities`, + checkRequirement: () => Currency.eternities.gte(1e7), + checkEvent: [GAME_EVENT.ETERNITY_RESET_AFTER, GAME_EVENT.REALITY_FIRST_UNLOCKED], + description: "Gain Eternities per second equal to your Reality count", + automatorPoints: 5, + shortDescription: () => `Continuous Eternity generation`, + effect: () => Currency.realities.value * Ra.unlocks.continuousTTBoost.effects.eternity.effectOrDefault(1), + formatEffect: value => `${format(value)} per second` + }, + { + name: "The Paradoxical Forever", + id: 15, + cost: 50, + requirement: () => `Have ${format(DC.E10)} Eternity Points without purchasing + the ${formatX(5)} Eternity Point upgrade`, + hasFailed: () => player.epmultUpgrades !== 0, + checkRequirement: () => Currency.eternityPoints.exponent >= 10 && player.epmultUpgrades === 0, + checkEvent: GAME_EVENT.ETERNITY_RESET_AFTER, + description: () => `Boost Tachyon Particle gain based on ${formatX(5)} Eternity Point multiplier`, + effect: () => Math.max(Math.sqrt(Decimal.log10(EternityUpgrade.epMult.effectValue)) / 3, 1), + formatEffect: value => formatX(value, 2, 2) + }, + { + name: "Disparity of Rarity", + id: 16, + cost: 1500, + requirement: () => `Reality with ${formatInt(4)} Glyphs equipped of uncommon or better rarity + (${formatInt(Glyphs.activeList.countWhere(g => g && g.strength >= 1.5))} equipped)`, + hasFailed: () => { + const availableGlyphs = Glyphs.inventory.countWhere(g => g && g.strength >= 1.5); + const equipped = Glyphs.activeList.countWhere(g => g.strength >= 1.5); + const availableSlots = Glyphs.activeSlotCount - Glyphs.activeList.length; + return equipped + Math.min(availableGlyphs, availableSlots) < 4; }, - { - name: "Paradoxically Attain", - id: 8, - cost: 15, - requirement: "Get to Eternity without any automatic Achievements", - hasFailed: () => player.reality.gainedAutoAchievements, - checkRequirement: () => !player.reality.gainedAutoAchievements, - checkEvent: GAME_EVENT.ETERNITY_RESET_BEFORE, - description: "Tachyon Particle gain is boosted based on Achievement multiplier", - effect: () => Math.sqrt(Achievements.power), - formatEffect: value => formatX(value, 2, 2) + checkRequirement: () => Glyphs.activeList.countWhere(g => g.strength >= 1.5) === 4, + checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, + description: "Improve the Glyph rarity formula", + effect: 1.3, + formatCost: value => format(value, 1, 0) + }, + { + name: "Duplicity of Potency", + id: 17, + cost: 1500, + requirement: () => `Reality with ${formatInt(4)} Glyphs equipped, each having at least ${formatInt(2)} effects + (${formatInt(Glyphs.activeList.countWhere(g => g && countValuesFromBitmask(g.effects) >= 2))} equipped)`, + hasFailed: () => { + const availableGlyphs = Glyphs.inventory.countWhere(g => g && countValuesFromBitmask(g.effects) >= 2); + const equipped = Glyphs.activeList.countWhere(g => countValuesFromBitmask(g.effects) >= 2); + const availableSlots = Glyphs.activeSlotCount - Glyphs.activeList.length; + return equipped + Math.min(availableGlyphs, availableSlots) < 4; }, - { - name: "Linguistically Expand", - id: 9, - cost: 15, - requirement: () => `Eternity for ${format("1e4000")} Eternity Points using - only a single level ${formatInt(3)}+ Glyph.`, - hasFailed: () => { - const invalidEquippedGlyphs = Glyphs.activeList.length > 1 || - (Glyphs.activeList.length === 1 && Glyphs.activeList[0].level < 3); - const hasValidGlyphInInventory = Glyphs.inventory.countWhere(g => g && g.level >= 3) > 0; - return invalidEquippedGlyphs || (Glyphs.activeList.length === 0 && !hasValidGlyphInInventory); - }, - checkRequirement: () => Currency.eternityPoints.exponent >= 4000 && - Glyphs.activeList.length === 1 && Glyphs.activeList[0].level >= 3, - checkEvent: GAME_EVENT.ETERNITY_RESET_AFTER, - description: "Gain another Glyph slot", - effect: () => 1 + checkRequirement: () => Glyphs.activeList.countWhere(g => countValuesFromBitmask(g.effects) >= 2) === 4, + checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, + description: () => `${formatPercents(0.5)} chance to get an additional effect on Glyphs`, + effect: 0.5, + formatCost: value => format(value, 1, 0) + }, + { + name: "Measure of Forever", + id: 18, + cost: 1500, + requirement: () => `Reality with ${formatInt(4)} Glyphs equipped, each at level ${formatInt(10)} or higher + (${formatInt(Glyphs.activeList.countWhere(g => g && g.level >= 10))} equipped)`, + hasFailed: () => { + const availableGlyphs = Glyphs.inventory.countWhere(g => g && g.level >= 10); + const equipped = Glyphs.activeList.countWhere(g => g.level >= 10); + const availableSlots = Glyphs.activeSlotCount - Glyphs.activeList.length; + return equipped + Math.min(availableGlyphs, availableSlots) < 4; }, - { - name: "Existentially Prolong", - id: 10, - cost: 15, - requirement: () => `Complete your first Eternity with at least ${formatPostBreak(DC.E450)} Infinity Points`, - hasFailed: () => !player.requirementChecks.reality.noEternities, - checkRequirement: () => Currency.infinityPoints.exponent >= 450 && - player.requirementChecks.reality.noEternities, - checkEvent: GAME_EVENT.ETERNITY_RESET_BEFORE, - description: () => `Start every Reality with ${formatInt(100)} Eternities (also applies to current Reality)`, - automatorPoints: 15, - shortDescription: () => `Start with ${formatInt(100)} Eternities`, - effect: () => 100 - }, - { - name: "The Boundless Flow", - id: 11, - cost: 50, - requirement: () => `${format(Currency.infinitiesBanked.value, 2)}/${format(DC.E12)} Banked Infinities`, - checkRequirement: () => Currency.infinitiesBanked.exponent >= 12, - checkEvent: [GAME_EVENT.ETERNITY_RESET_AFTER, GAME_EVENT.REALITY_FIRST_UNLOCKED], - description: "Every second, gain 10% of the Infinities you would normally gain by Infinitying", - automatorPoints: 5, - shortDescription: () => `Continuous Infinity generation`, - effect: () => gainedInfinities().times(0.1), - formatEffect: value => `${format(value)} per second` - }, - { - name: "The Knowing Existence", - id: 12, - cost: 50, - requirement: () => `Eternity for ${format(DC.E70)} Eternity Points without Eternity Challenge 1`, - hasFailed: () => EternityChallenge(1).completions !== 0, - checkRequirement: () => Currency.eternityPoints.exponent >= 70 && EternityChallenge(1).completions === 0, - checkEvent: GAME_EVENT.ETERNITY_RESET_AFTER, - description: "Eternity Point multiplier based on Reality and Time Theorem count", - effect: () => Currency.timeTheorems.value - .minus(DC.E3).clampMin(2) - .pow(Math.log2(Math.min(Currency.realities.value, 1e4))).clampMin(1), - formatEffect: value => formatX(value, 2, 2) - }, - { - name: "The Telemechanical Process", - id: 13, - cost: 50, - requirement: () => `Eternity for ${format(DC.E4000)} Eternity Points without Time Dimensions 5-8`, - hasFailed: () => !Array.range(5, 4).every(i => TimeDimension(i).amount.equals(0)), - checkRequirement: () => Currency.eternityPoints.exponent >= 4000 && - Array.range(5, 4).every(i => TimeDimension(i).amount.equals(0)), - checkEvent: GAME_EVENT.ETERNITY_RESET_AFTER, - description: () => `Unlock Time Dimension, ${formatX(5)} Eternity Point multiplier, - and improved Eternity autobuyers`, - automatorPoints: 10, - shortDescription: () => `TD and ${formatX(5)} EP Autobuyers, improved Eternity Autobuyer`, - }, - { - name: "The Eternal Flow", - id: 14, - cost: 50, - requirement: () => `${format(Currency.eternities.value, 2)}/${format(1e7)} Eternities`, - checkRequirement: () => Currency.eternities.gte(1e7), - checkEvent: [GAME_EVENT.ETERNITY_RESET_AFTER, GAME_EVENT.REALITY_FIRST_UNLOCKED], - description: "Gain Eternities per second equal to your Reality count", - automatorPoints: 5, - shortDescription: () => `Continuous Eternity generation`, - effect: () => Currency.realities.value * RA_UNLOCKS.TT_BOOST.effect.eternity(), - formatEffect: value => `${format(value)} per second` - }, - { - name: "The Paradoxical Forever", - id: 15, - cost: 50, - requirement: () => `Eternity for ${format(DC.E10)} Eternity Points without purchasing - the ${formatX(5)} Eternity Point upgrade`, - hasFailed: () => player.epmultUpgrades !== 0, - checkRequirement: () => Currency.eternityPoints.exponent >= 10 && player.epmultUpgrades === 0, - checkEvent: GAME_EVENT.ETERNITY_RESET_AFTER, - description: () => `Boost Tachyon Particle gain based on ${formatX(5)} Eternity Point multiplier`, - effect: () => Math.max(Math.sqrt(Decimal.log10(EternityUpgrade.epMult.effectValue)) / 3, 1), - formatEffect: value => formatX(value, 2, 2) - }, - { - name: "Disparity of Rarity", - id: 16, - cost: 1500, - requirement: () => `Reality with ${formatInt(4)} Glyphs equipped of uncommon or better rarity - (${formatInt(Glyphs.activeList.countWhere(g => g && g.strength >= 1.5))} equipped)`, - hasFailed: () => { - const availableGlyphs = Glyphs.inventory.countWhere(g => g && g.strength >= 1.5); - const equipped = Glyphs.activeList.countWhere(g => g.strength >= 1.5); - const availableSlots = Glyphs.activeSlotCount - Glyphs.activeList.length; - return equipped + Math.min(availableGlyphs, availableSlots) < 4; - }, - checkRequirement: () => Glyphs.activeList.countWhere(g => g.strength >= 1.5) === 4, - checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, - description: "Improve the Glyph rarity formula", - effect: 1.3, - formatCost: value => format(value, 1, 0) - }, - { - name: "Duplicity of Potency", - id: 17, - cost: 1500, - requirement: () => `Reality with ${formatInt(4)} Glyphs equipped, each having at least ${formatInt(2)} effects - (${formatInt(Glyphs.activeList.countWhere(g => g && countValuesFromBitmask(g.effects) >= 2))} equipped)`, - hasFailed: () => { - const availableGlyphs = Glyphs.inventory.countWhere(g => g && countValuesFromBitmask(g.effects) >= 2); - const equipped = Glyphs.activeList.countWhere(g => countValuesFromBitmask(g.effects) >= 2); - const availableSlots = Glyphs.activeSlotCount - Glyphs.activeList.length; - return equipped + Math.min(availableGlyphs, availableSlots) < 4; - }, - checkRequirement: () => Glyphs.activeList.countWhere(g => countValuesFromBitmask(g.effects) >= 2) === 4, - checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, - description: () => `${formatPercents(0.5)} chance to get an additional effect on Glyphs`, - effect: 0.5, - formatCost: value => format(value, 1, 0) - }, - { - name: "Measure of Forever", - id: 18, - cost: 1500, - requirement: () => `Reality with ${formatInt(4)} Glyphs equipped, each at level ${formatInt(10)} or higher - (${formatInt(Glyphs.activeList.countWhere(g => g && g.level >= 10))} equipped)`, - hasFailed: () => { - const availableGlyphs = Glyphs.inventory.countWhere(g => g && g.level >= 10); - const equipped = Glyphs.activeList.countWhere(g => g.level >= 10); - const availableSlots = Glyphs.activeSlotCount - Glyphs.activeList.length; - return equipped + Math.min(availableGlyphs, availableSlots) < 4; - }, - checkRequirement: () => Glyphs.activeList.countWhere(g => g.level >= 10) === 4, - checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, - description: "Eternity count boosts Glyph level", - effect: () => Math.max(Math.sqrt(Currency.eternities.value.plus(1).log10()) * 0.45, 1), - formatCost: value => format(value, 1, 0) - }, - { - name: "Scour to Empower", - id: 19, - cost: 1500, - requirement: () => `Have a total of ${formatInt(30)} or more Glyphs at once - (You have ${formatInt(Glyphs.allGlyphs.countWhere(g => g))})`, - hasFailed: () => Glyphs.allGlyphs.countWhere(g => g) < 30, - checkRequirement: () => Glyphs.allGlyphs.countWhere(g => g) >= 30, - checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, - description: "You can sacrifice Glyphs for permanent bonuses (Shift + click)", - formatCost: value => format(value, 1, 0) - }, - { - name: "Parity of Singularity", - id: 20, - cost: 1500, - requirement: () => `${formatInt(1)} year total play time and the Black Hole unlocked - (Currently: ${Time.totalTimePlayed.toStringShort(false)})`, - hasFailed: () => !BlackHole(1).isUnlocked && Currency.realityMachines.lt(100), - checkRequirement: () => Time.totalTimePlayed.totalYears >= 1 && BlackHole(1).isUnlocked, - checkEvent: GAME_EVENT.GAME_TICK_AFTER, - description: "Unlock Black Hole 2", - automatorPoints: 10, - shortDescription: () => `Second Black Hole`, - formatCost: value => format(value, 1, 0) - }, - { - name: "Cosmic Conglomerate", - id: 21, - cost: 100000, - requirement: () => `${formatInt(Replicanti.galaxies.total + player.galaxies + - player.dilation.totalTachyonGalaxies)}/${formatInt(2800)} total Galaxies from all types`, - checkRequirement: () => - Replicanti.galaxies.total + player.galaxies + player.dilation.totalTachyonGalaxies >= 2800, - checkEvent: GAME_EVENT.GAME_TICK_AFTER, - description: () => `Remote Antimatter Galaxy scaling is moved to ${formatInt(1e5)} galaxies`, - effect: 1e5 - }, - { - name: "Temporal Transcendence", - id: 22, - cost: 100000, - requirement: () => `${format(Currency.timeShards.value, 1)}/${format(DC.E28000)} Time Shards`, - checkRequirement: () => Currency.timeShards.exponent >= 28000, - checkEvent: GAME_EVENT.GAME_TICK_AFTER, - description: "Time Dimension multiplier based on days spent in this Reality", - effect: () => Decimal.pow10(Math.pow(1 + 2 * Math.log10(Time.thisReality.totalDays + 1), 1.6)), - formatEffect: value => formatX(value, 2, 2) - }, - { - name: "Replicative Rapidity", - id: 23, - cost: 100000, - requirement: () => `Reality in under ${formatInt(15)} minutes (Best: ${Time.bestReality.toStringShort()})`, - hasFailed: () => Time.thisReality.totalMinutes >= 15, - checkRequirement: () => Time.thisReality.totalMinutes < 15, - checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, - description: "Replicanti speed is boosted based on your fastest Reality", - effect: () => 15 / Math.clamp(Time.bestReality.totalMinutes, 1 / 12, 15), - cap: 180, - formatEffect: value => formatX(value, 2, 2) - }, - { - name: "Synthetic Symbolism", - id: 24, - cost: 100000, - requirement: () => `Reality for ${formatInt(5000)} Reality Machines without Glyphs`, - hasFailed: () => Glyphs.activeList.length > 0, - checkRequirement: () => MachineHandler.gainedRealityMachines.gte(5000) && Glyphs.activeList.length === 0, - checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, - description: "Gain another Glyph slot", - effect: () => 1 - }, - { - name: "Effortless Existence", - id: 25, - cost: 100000, - requirement: () => `Reach ${format(DC.E11111)} EP (Best: ${format(player.records.bestReality.bestEP, 2)} EP)`, - checkRequirement: () => player.records.bestReality.bestEP.exponent >= 11111, - checkEvent: GAME_EVENT.ETERNITY_RESET_AFTER, - description: "Unlock the Reality autobuyer and Automator command", - automatorPoints: 100, - shortDescription: () => `Reality Autobuyer`, - }, - ]; -}()); + checkRequirement: () => Glyphs.activeList.countWhere(g => g.level >= 10) === 4, + checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, + description: "Eternity count boosts Glyph level", + effect: () => Math.max(Math.sqrt(Currency.eternities.value.plus(1).log10()) * 0.45, 1), + formatCost: value => format(value, 1, 0) + }, + { + name: "Scour to Empower", + id: 19, + cost: 1500, + requirement: () => `Have a total of ${formatInt(30)} or more Glyphs at once + (You have ${formatInt(Glyphs.allGlyphs.countWhere(g => g))})`, + hasFailed: () => Glyphs.allGlyphs.countWhere(g => g) < 30, + checkRequirement: () => Glyphs.allGlyphs.countWhere(g => g) >= 30, + checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, + description: "You can sacrifice Glyphs for permanent bonuses (Shift + click)", + formatCost: value => format(value, 1, 0) + }, + { + name: "Parity of Singularity", + id: 20, + cost: 1500, + requirement: () => `${formatInt(1)} year total play time and the Black Hole unlocked + (Currently: ${Time.totalTimePlayed.toStringShort(false)})`, + hasFailed: () => !BlackHole(1).isUnlocked && Currency.realityMachines.lt(100), + checkRequirement: () => Time.totalTimePlayed.totalYears >= 1 && BlackHole(1).isUnlocked, + checkEvent: GAME_EVENT.GAME_TICK_AFTER, + description: "Unlock Black Hole 2", + automatorPoints: 10, + shortDescription: () => `Second Black Hole`, + formatCost: value => format(value, 1, 0) + }, + { + name: "Cosmic Conglomerate", + id: 21, + cost: 100000, + requirement: () => `${formatInt(Replicanti.galaxies.total + player.galaxies + + player.dilation.totalTachyonGalaxies)}/${formatInt(2800)} total Galaxies from all types`, + checkRequirement: () => + Replicanti.galaxies.total + player.galaxies + player.dilation.totalTachyonGalaxies >= 2800, + checkEvent: GAME_EVENT.GAME_TICK_AFTER, + description: () => `Remote Antimatter Galaxy scaling is moved to ${formatInt(1e5)} galaxies`, + effect: 1e5 + }, + { + name: "Temporal Transcendence", + id: 22, + cost: 100000, + requirement: () => `${format(Currency.timeShards.value, 1)}/${format(DC.E28000)} Time Shards`, + checkRequirement: () => Currency.timeShards.exponent >= 28000, + checkEvent: GAME_EVENT.GAME_TICK_AFTER, + description: "Time Dimension multiplier based on days spent in this Reality", + effect: () => Decimal.pow10(Math.pow(1 + 2 * Math.log10(Time.thisReality.totalDays + 1), 1.6)), + formatEffect: value => formatX(value, 2, 2) + }, + { + name: "Replicative Rapidity", + id: 23, + cost: 100000, + requirement: () => `Reality in under ${formatInt(15)} minutes (Best: ${Time.bestReality.toStringShort()})`, + hasFailed: () => Time.thisReality.totalMinutes >= 15, + checkRequirement: () => Time.thisReality.totalMinutes < 15, + checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, + description: "Replicanti speed is boosted based on your fastest Reality", + effect: () => 15 / Math.clamp(Time.bestReality.totalMinutes, 1 / 12, 15), + cap: 180, + formatEffect: value => formatX(value, 2, 2) + }, + { + name: "Synthetic Symbolism", + id: 24, + cost: 100000, + requirement: () => `Reality for ${formatInt(5000)} Reality Machines without Glyphs`, + hasFailed: () => Glyphs.activeList.length > 0, + checkRequirement: () => MachineHandler.gainedRealityMachines.gte(5000) && Glyphs.activeList.length === 0, + checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, + description: "Gain another Glyph slot", + effect: () => 1 + }, + { + name: "Effortless Existence", + id: 25, + cost: 100000, + requirement: () => `Reach ${format(DC.E11111)} EP (Best: ${format(player.records.bestReality.bestEP, 2)} EP)`, + checkRequirement: () => player.records.bestReality.bestEP.exponent >= 11111, + checkEvent: GAME_EVENT.ETERNITY_RESET_AFTER, + description: "Unlock the Reality autobuyer and Automator command", + automatorPoints: 100, + shortDescription: () => `Reality Autobuyer`, + }, +]; diff --git a/javascripts/core/secret-formula/script-templates.js b/javascripts/core/secret-formula/script-templates.js index 46d2e3f8b..1a1272131 100644 --- a/javascripts/core/secret-formula/script-templates.js +++ b/javascripts/core/secret-formula/script-templates.js @@ -1,5 +1,5 @@ -import { GameDatabase } from "./game-database.js"; import { AutobuyerInputFunctions } from "@/components/tabs/autobuyers/AutobuyerInput"; +import { GameDatabase } from "./game-database"; GameDatabase.reality.automator.templates = { /** @@ -18,8 +18,11 @@ GameDatabase.reality.automator.templates = { name: "tree", isValidString: str => { const validImport = TimeStudyTree.isValidImportString(str); - const presetName = str.match(/^PRESET (.{1,4})$/u); - const validPreset = presetName ? player.timestudy.presets.map(p => p.name).includes(presetName[1]) : false; + const preset = str.match(/^(NAME (.{1,4})|ID (\d))$/u); + const validPreset = preset ? ( + player.timestudy.presets.some(p => p.name === preset[2]) || + (Number(preset[3]) > 0 && Number(preset[3]) < 7) + ) : false; return validImport || validPreset; }, }, diff --git a/javascripts/core/secret-formula/shop-purchases.js b/javascripts/core/secret-formula/shop-purchases.js index fbc167e51..4f9d990cd 100644 --- a/javascripts/core/secret-formula/shop-purchases.js +++ b/javascripts/core/secret-formula/shop-purchases.js @@ -1,4 +1,4 @@ -import { GameDatabase } from "./game-database.js"; +import { GameDatabase } from "./game-database"; GameDatabase.shopPurchases = { dimPurchases: { @@ -10,19 +10,58 @@ GameDatabase.shopPurchases = { IPPurchases: { key: "IPPurchases", cost: 40, - description: "Double your Infinity Point gain from all sources. (additive) ", + description: "Double your Infinity Point gain from all sources. (additive)", multiplier: purchases => (purchases === 0 ? 1 : 2 * purchases), }, EPPurchases: { key: "EPPurchases", cost: 50, - description: "Triple your Eternity Point gain from all sources. (additive) ", + description: "Triple your Eternity Point gain from all sources. (additive)", multiplier: purchases => (purchases === 0 ? 1 : 3 * purchases), }, + RMPurchases: { + key: "RMPurchases", + cost: 60, + description: "Increase your Reality Machine gain by 100%. (additive)", + multiplier: purchases => purchases + 1, + formatEffect: x => formatX(x, 2), + }, allDimPurchases: { key: "allDimPurchases", cost: 60, - description: "Double ALL Dimension multipliers (Antimatter, Infinity, Time) (multiplicative until 32x). Forever. ", - multiplier: purchases => (purchases > 4 ? 32 * (purchases - 4) : Math.pow(2, purchases)), - } + description: "Double ALL Dimension multipliers (Antimatter, Infinity, Time) (multiplicative until 32x). Forever.", + multiplier: purchases => (purchases > 4 ? 32 + (purchases - 5) * 2 : Math.pow(2, purchases)), + }, + replicantiPurchases: { + key: "replicantiPurchases", + cost: 60, + description: "Increase your Replicanti gain by 50%. (additive)", + multiplier: purchases => (purchases === 0 ? 1 : 1 + 0.5 * purchases), + formatEffect: x => formatX(x, 2, 1), + }, + dilatedTimePurchases: { + key: "dilatedTimePurchases", + cost: 40, + description: "Increase your Dilated Time gain by 50%. (additive)", + multiplier: purchases => (purchases === 0 ? 1 : 1 + 0.5 * purchases), + formatEffect: x => formatX(x, 2, 1), + }, + smallTimeSkip: { + key: "smallTimeSkip", + cost: 10, + description: "Get 6 hours worth of offline production. (Autobuyers don't work at full speed)", + singleUse: true, + onPurchase: () => { + kong.purchaseTimeSkip(); + } + }, + bigTimeSkip: { + key: "bigTimeSkip", + cost: 20, + description: "Get 24 hours worth of offline production. (Autobuyers don't work at full speed)", + singleUse: true, + onPurchase: () => { + kong.purchaseLongerTimeSkip(); + } + }, }; diff --git a/javascripts/core/secret-formula/speedrun-milestones.js b/javascripts/core/secret-formula/speedrun-milestones.js index 02e675cac..d435ef202 100644 --- a/javascripts/core/secret-formula/speedrun-milestones.js +++ b/javascripts/core/secret-formula/speedrun-milestones.js @@ -1,4 +1,4 @@ -import { GameDatabase } from "./game-database.js"; +import { GameDatabase } from "./game-database"; GameDatabase.speedrunMilestones = [ { @@ -86,7 +86,7 @@ GameDatabase.speedrunMilestones = [ key: "allEternityMilestones", name: "All Eternity Milestones", description: "Unlock all Eternity Milestones", - checkRequirement: () => EternityMilestones.all.every(m => m.isReached), + checkRequirement: () => EternityMilestone.all.every(m => m.isReached), checkEvent: GAME_EVENT.ETERNITY_RESET_AFTER, }, { @@ -164,8 +164,8 @@ GameDatabase.speedrunMilestones = [ { id: 21, key: "completeEnslavedReality", - name: "The Enslaved Ones' Reality", - description: "Complete The Enslaved Ones' Reality", + name: "The Nameless Ones' Reality", + description: "Complete The Nameless Ones' Reality", checkRequirement: () => Enslaved.isRunning, checkEvent: GAME_EVENT.REALITY_RESET_BEFORE, }, diff --git a/javascripts/core/secret-formula/tab-notifications.js b/javascripts/core/secret-formula/tab-notifications.js index 8f03eb1e9..58de3555b 100644 --- a/javascripts/core/secret-formula/tab-notifications.js +++ b/javascripts/core/secret-formula/tab-notifications.js @@ -1,5 +1,6 @@ -import { GameDatabase } from "./game-database.js"; -import { DC } from "../constants.js"; +import { DC } from "../constants"; + +import { GameDatabase } from "./game-database"; GameDatabase.tabNotifications = { firstInfinity: { @@ -107,7 +108,8 @@ GameDatabase.tabNotifications = { } ], condition: () => !PlayerProgress.realityUnlocked() && TimeStudy.reality.canBeBought, - events: [GAME_EVENT.ETERNITY_RESET_AFTER, GAME_EVENT.SAVE_CONVERTED_FROM_PREVIOUS_VERSION] + events: [GAME_EVENT.ETERNITY_RESET_AFTER, GAME_EVENT.SAVE_CONVERTED_FROM_PREVIOUS_VERSION, + GAME_EVENT.OFFLINE_CURRENCY_GAINED, GAME_EVENT.ACHIEVEMENT_UNLOCKED] }, blackHoleUnlock: { id: 8, @@ -143,7 +145,7 @@ GameDatabase.tabNotifications = { tab: "teresa" } ], - condition: () => player.celestials.teresa.pouredAmount !== 0 && Teresa.isUnlocked, + condition: () => player.celestials.teresa.pouredAmount === 0 && Teresa.isUnlocked, events: [GAME_EVENT.REALITY_UPGRADE_BOUGHT] }, alchemyUnlock: { @@ -161,4 +163,15 @@ GameDatabase.tabNotifications = { condition: () => player.celestials.ra.pets.effarig.level >= 2, events: [GAME_EVENT.GAME_TICK_AFTER] }, + newAutobuyer: { + id: 12, + tabsToHighLight: [ + { + parent: "automation", + tab: "autobuyers" + }, + ], + // Always externally triggered + condition: () => true, + }, }; diff --git a/javascripts/core/secret-formula/tabs.js b/javascripts/core/secret-formula/tabs.js index 8f256ffbe..3ef407045 100644 --- a/javascripts/core/secret-formula/tabs.js +++ b/javascripts/core/secret-formula/tabs.js @@ -1,4 +1,4 @@ -import { GameDatabase } from "./game-database.js"; +import { GameDatabase } from "./game-database"; GameDatabase.tabs = [ { @@ -160,6 +160,7 @@ GameDatabase.tabs = [ name: "Automation", id: 4, hideAt: 2.1, + condition: () => player.records.totalAntimatter.gte(1e40), hidable: true, subtabs: [ { @@ -322,7 +323,6 @@ GameDatabase.tabs = [ key: "reality", name: "Reality", hideAt: 2.3, - before: "RealityMachinesHeader", UIClass: "o-tab-btn--reality", condition: () => PlayerProgress.realityUnlocked() || TimeStudy.reality.isBought, id: 8, @@ -375,7 +375,7 @@ GameDatabase.tabs = [ name: "Glyph Alchemy", symbol: "", component: "AlchemyTab", - condition: () => Ra.has(RA_UNLOCKS.GLYPH_ALCHEMY), + condition: () => Ra.unlocks.unlockGlyphAlchemy.canBeApplied, id: 5, hidable: true, }, @@ -411,14 +411,14 @@ GameDatabase.tabs = [ name: "Effarig", symbol: "Ϙ", component: "EffarigTab", - condition: () => Teresa.has(TERESA_UNLOCKS.EFFARIG), + condition: () => TeresaUnlocks.effarig.isUnlocked, id: 2, hidable: true, }, { key: "enslaved", - name: "The Enslaved Ones", - symbol: "", + name: "The Nameless Ones", + symbol: "
\uf0c1
", component: "EnslavedTab", condition: () => EffarigUnlock.eternity.isUnlocked, id: 3, @@ -438,7 +438,7 @@ GameDatabase.tabs = [ name: "Ra", symbol: "", component: "RaTab", - condition: () => V.has(V_UNLOCKS.RA_UNLOCK), + condition: () => VUnlocks.raUnlock.isUnlocked, id: 5, hidable: true, }, @@ -466,7 +466,7 @@ GameDatabase.tabs = [ key: "shop", name: "Shop", newUIClass: "shop", - hideAt: 2.4, + hideAt: 1.5, condition: () => 1===1 /*kong.enabled || player.IAP.totalSTD > 0, ||*/, id: 10, hidable: true, diff --git a/javascripts/core/speedrun.js b/javascripts/core/speedrun.js index 9faddfd61..b07f72f69 100644 --- a/javascripts/core/speedrun.js +++ b/javascripts/core/speedrun.js @@ -1,5 +1,5 @@ import { GameDatabase } from "./secret-formula/game-database"; -import { GameMechanicState } from "./game-mechanics/index.js"; +import { GameMechanicState } from "./game-mechanics/index"; export const Speedrun = { unlock() { @@ -8,7 +8,7 @@ export const Speedrun = { Modal.message.show(`You have unlocked Speedrun Mode! This allows you to start a new save file with some slight changes which can be helpful if you're trying to complete the game as quickly as possible. The option to start a Speedrun Save is now available in the Options tab, under Saving. Choosing to start a Speedrun Save - will provide you with another modal with more in-depth information.`); + will provide you with another modal with more in-depth information.`, {}, 3); player.speedrun.isUnlocked = true; }, // If a name isn't given, choose a somewhat-likely-to-be-unique big number instead @@ -40,6 +40,7 @@ export const Speedrun = { // Some time elapses after the reset and before the UI is actually ready, which ends up getting "counted" as offline player.speedrun.offlineTimeUsed = 0; + GameStorage.save(); }, // Speedruns are initially paused until startTimer is called, which happens as soon as the player purchases a AD or // uses the Konami code. Until then, they're free to do whatever they want with the UI diff --git a/javascripts/core/storage/base64-binary.js b/javascripts/core/storage/base64-binary.js index 4c667a750..9bc6371da 100644 --- a/javascripts/core/storage/base64-binary.js +++ b/javascripts/core/storage/base64-binary.js @@ -1,4 +1,4 @@ -/* eslint-disable no-bitwise,no-param-reassign */ +/* eslint-disable no-param-reassign */ // Copyright (c) 2011, Daniel Guerrero // All rights reserved. diff --git a/javascripts/core/storage/cloud-saving.js b/javascripts/core/storage/cloud-saving.js index b6b000ca1..52374841e 100644 --- a/javascripts/core/storage/cloud-saving.js +++ b/javascripts/core/storage/cloud-saving.js @@ -1,9 +1,16 @@ +/* eslint-disable import/extensions */ import pako from "pako/dist/pako.esm.mjs"; -import { decodeBase64Binary } from "./base64-binary"; -import { ProgressChecker } from "./progress-checker.js"; +/* eslint-enable import/extensions */ + +import { get, getDatabase, ref, set } from "firebase/database"; +import { getAuth, GoogleAuthProvider, signInWithPopup, signOut } from "firebase/auth"; import { initializeApp } from "firebase/app"; import { getAuth, signInWithPopup, signInWithEmailAndPassword, createUserWithEmailAndPassword, signOut, GoogleAuthProvider } from "firebase/auth"; import { getDatabase, ref, get, set } from "firebase/database"; +import { sha512_256 } from "js-sha512"; + +import { decodeBase64Binary } from "./base64-binary"; +import { ProgressChecker } from "./progress-checker"; const firebaseConfig = { apiKey: "AIzaSyDuRTTluAFufmvw1zxGH6fsyEHmmbu8IHI", @@ -24,6 +31,7 @@ export const Cloud = { user: null, hasSeenSavingConflict: false, shouldOverwriteCloudSave: true, + lastCloudHash: null, get loggedIn() { return this.user !== null; @@ -64,6 +72,16 @@ export const Cloud = { } }, + compareSaves(cloud, local, hash) { + return { + farther: ProgressChecker.compareSaveProgress(cloud, local), + older: ProgressChecker.compareSaveTimes(cloud, local), + diffSTD: (cloud?.IAP?.totalSTD ?? 0) - (local?.IAP?.totalSTD ?? 0), + differentName: cloud?.options.saveFileName !== local?.options.saveFileName, + hashMismatch: hash && this.lastCloudHash !== hash, + }; + }, + async saveCheck() { GameIntervals.checkCloudSave.restart(); const save = await this.load(); @@ -74,10 +92,7 @@ export const Cloud = { const saveId = GameStorage.currentSlot; const cloudSave = root.saves[saveId]; const localSave = GameStorage.saves[saveId]; - const saveComparison = { - farther: ProgressChecker.compareSaveProgress(cloudSave, localSave), - older: ProgressChecker.compareSaveTimes(cloudSave, localSave) - }; + const saveComparison = this.compareSaves(cloudSave, localSave, sha512_256(save)); // eslint-disable-next-line no-loop-func const overwriteAndSendCloudSave = () => { @@ -87,8 +102,12 @@ export const Cloud = { // Bring up the modal if cloud saving will overwrite a cloud save which is older or possibly farther const hasBoth = cloudSave && localSave; - const hasConflict = hasBoth && (saveComparison.older === -1 || saveComparison.farther !== 1); - if (hasConflict && !this.hasSeenSavingConflict) { + // NOTE THIS CHECK IS INTENTIONALLY DIFFERENT FROM THE LOAD CHECK + // Hash mismatch check should be separate from the others because otherwise it only ever shows up on the first + // mismatch; there are situations (eg. two devices actively saving) which can cause this to happen repeatedly. + const hasConflict = hasBoth && (saveComparison.older === -1 || saveComparison.farther !== 1 || + saveComparison.diffSTD > 0 || saveComparison.differentName); + if ((hasConflict && !this.hasSeenSavingConflict) || saveComparison.hashMismatch) { Modal.addCloudConflict(saveId, saveComparison, cloudSave, localSave, overwriteAndSendCloudSave); Modal.cloudSaveConflict.show(); } else if (!hasConflict || (this.hasSeenSavingConflict && this.shouldOverwriteCloudSave)) { @@ -105,7 +124,9 @@ export const Cloud = { saves: GameStorage.saves, }; - set(ref(this.db, `users/${this.user.id}/web`), GameSaveSerializer.serialize(root)); + const toSave = GameSaveSerializer.serialize(root); + this.lastCloudHash = sha512_256(toSave); + set(ref(this.db, `users/${this.user.id}/web`), toSave); GameUI.notify.info(`Game saved (slot ${slot + 1}) to cloud`);/* with user ${this.user.displayName}`);*/ }, @@ -118,10 +139,7 @@ export const Cloud = { const saveId = GameStorage.currentSlot; const cloudSave = root.saves[saveId]; const localSave = GameStorage.saves[saveId]; - const saveComparison = { - farther: ProgressChecker.compareSaveProgress(cloudSave, localSave), - older: ProgressChecker.compareSaveTimes(cloudSave, localSave) - }; + const saveComparison = this.compareSaves(cloudSave, localSave); // eslint-disable-next-line no-loop-func const overwriteLocalSave = () => { @@ -131,7 +149,9 @@ export const Cloud = { // Bring up the modal if cloud loading will overwrite a local save which is older or possibly farther const hasBoth = cloudSave && localSave; - if (hasBoth && (saveComparison.older === 1 || saveComparison.farther !== -1)) { + const hasConflict = hasBoth && (saveComparison.older === 1 || saveComparison.farther !== -1 || + saveComparison.diffSTD < 0 || saveComparison.differentName); + if (hasConflict) { Modal.addCloudConflict(saveId, saveComparison, cloudSave, localSave, overwriteLocalSave); Modal.cloudLoadConflict.show(); } else { diff --git a/javascripts/core/storage/dev-migrations.js b/javascripts/core/storage/dev-migrations.js index 9a933a7a4..4d71dc940 100644 --- a/javascripts/core/storage/dev-migrations.js +++ b/javascripts/core/storage/dev-migrations.js @@ -1,8 +1,7 @@ -import { GameStorage } from "./storage.js"; +import { GameStorage } from "./storage"; function arrayToBits(array) { let bits = 0; - // eslint-disable-next-line no-bitwise for (const id of array) bits |= (1 << id); return bits; } @@ -288,7 +287,6 @@ GameStorage.devMigrations = { player.reality.upgradeBits = arrayToBits(player.reality.upg); delete player.reality.upg; } - // eslint-disable-next-line no-bitwise if ((player.reality.upgradeBits & (1 << 25)) === 0) { player.realityBuyer.isOn = false; } @@ -358,8 +356,7 @@ GameStorage.devMigrations = { for (const effect of orderedEffectList) { const typeEffect = separateEffectKey(effect); if (glyph.type === typeEffect[0] && glyph.effects[typeEffect[1]] !== undefined) { - // eslint-disable-next-line no-bitwise - effectBitmask += 1 << GameDatabase.reality.glyphEffects[effect].bitmaskIndex; + effectBitmask += 1 << GlyphEffects[effect].bitmaskIndex; } } glyph.effects = effectBitmask; @@ -600,7 +597,7 @@ GameStorage.devMigrations = { pet.level = Math.clampMax(pet.level, 25); } delete player.celestials.ra.compression; - if (Ra.has(RA_UNLOCKS.ALWAYS_GAMESPEED)) { + if (Ra.unlocks.allGamespeedGlyphs.canBeApplied) { const allGlyphs = player.reality.glyphs.active .concat(player.reality.glyphs.inventory); for (const glyph of allGlyphs) { @@ -751,7 +748,6 @@ GameStorage.devMigrations = { } }, player => { - // eslint-disable-next-line no-bitwise player.celestials.ra.unlockBits &= ~(1 << 29); }, player => { @@ -975,7 +971,6 @@ GameStorage.devMigrations = { let reqBitmask = 0; for (let i = 0; i <= player.reality.upgReqs.length; i++) { - // eslint-disable-next-line no-bitwise if (player.reality.upgReqs[i]) reqBitmask |= (1 << i); } player.reality.upgReqs = reqBitmask; @@ -1218,42 +1213,29 @@ GameStorage.devMigrations = { player.celestials.pelle.galaxyGenerator.unlocked = player.celestials.pelle.galaxyGenerator.generatedGalaxies > 0; }, player => { - // eslint-disable-next-line no-bitwise if (player.celestials.pelle.doomed) player.achievementBits[17] |= 1; - // eslint-disable-next-line no-bitwise if (player.celestials.pelle.upgrades.has(4)) player.achievementBits[17] |= 2; if (player.celestials.pelle.doomed && player.challenge.infinity.completedBits === 510) { - // eslint-disable-next-line no-bitwise player.achievementBits[17] |= (1 << 2); } - // eslint-disable-next-line no-bitwise if (player.timestudy.studies.compact().includes(181)) player.achievementBits[17] |= (1 << 5); }, player => { - // eslint-disable-next-line no-bitwise player.achievementBits[16] |= (player.achievementBits[16] & (1 << 4)) << 3; - // eslint-disable-next-line no-bitwise player.achievementBits[16] &= ~(1 << 4); - // eslint-disable-next-line no-bitwise player.achievementBits[16] |= (player.achievementBits[16] & (1 << 2)) << 2; - // eslint-disable-next-line no-bitwise player.achievementBits[16] &= ~(1 << 2); }, player => { - // eslint-disable-next-line no-bitwise player.achievementBits[17] &= ~(1 << 5); if (player.timestudy.studies.compact().includes(181) && player.celestials.pelle.doomed) { - // eslint-disable-next-line no-bitwise player.achievementBits[17] |= (1 << 5); } }, player => { - // eslint-disable-next-line no-bitwise if (player.celestials.pelle.doomed && (player.challenge.infinity.completedBits & (1 << 5)) !== 0) { - // eslint-disable-next-line no-bitwise player.achievementBits[17] |= (1 << 2); } else { - // eslint-disable-next-line no-bitwise player.achievementBits[17] &= ~(1 << 2); } }, @@ -1313,7 +1295,170 @@ GameStorage.devMigrations = { player => { player.requirementChecks.permanent.emojiGalaxies = player.requirementChecks.permanent.cancerGalaxies; delete player.requirementChecks.permanent.cancerGalaxies; - } + }, + player => { + delete player.celestials.effarig.unlocksBits; + delete player.celestials.ra.unlocksBits; + }, + player => { + for (const script of Object.values(player.reality.automator.scripts)) { + script.id = parseInt(script.id, 10); + } + }, + player => { + player.secretUnlocks.themes.delete("S4Cancer"); + player.secretUnlocks.themes.add("S4Design"); + }, + player => { + player.reality.automator.state.editorScript = Number(player.reality.automator.state.editorScript); + // I'm not sure if there's any error with the type of topLevelScript, but better safe than sorry + player.reality.automator.state.topLevelScript = Number(player.reality.automator.state.topLevelScript); + }, + player => { + // Move dil upg no reset and tachyon particles no reset + if (player.celestials.pelle.upgrades.delete(20)) player.celestials.pelle.upgrades.add(21); + if (player.celestials.pelle.upgrades.delete(19)) player.celestials.pelle.upgrades.add(20); + + // Dimboost upgrade id was moved from 18 to 7 -- Make the corresponding change + // Galaxy upgrade was inserted at 11. 7-10 should only be moved forward 1 place + // and 10-17 2 places forward. + const hasDimboostsResetNothing = player.celestials.pelle.upgrades.delete(18); + for (let i = 17; i >= 10; i--) { + if (player.celestials.pelle.upgrades.delete(i)) player.celestials.pelle.upgrades.add(i + 2); + } + for (let i = 9; i >= 7; i--) { + if (player.celestials.pelle.upgrades.delete(i)) player.celestials.pelle.upgrades.add(i + 1); + } + if (hasDimboostsResetNothing) player.celestials.pelle.upgrades.add(7); + }, + player => { + const cel = player.celestials; + const convToBit = x => x.toBitmask() >> 1; + if (cel.teresa.quotes) player.celestials.teresa.quoteBits = convToBit(cel.teresa.quotes); + if (cel.effarig.quotes) player.celestials.effarig.quoteBits = convToBit(cel.effarig.quotes); + if (cel.enslaved.quotes) player.celestials.enslaved.quoteBits = convToBit(cel.enslaved.quotes); + if (cel.v.quotes) player.celestials.v.quoteBits = convToBit(cel.v.quotes); + if (cel.ra.quotes) player.celestials.ra.quoteBits = convToBit(cel.ra.quotes); + if (cel.laitela.quotes) player.celestials.laitela.quoteBits = convToBit(cel.laitela.quotes); + if (cel.pelle.quotes) player.celestials.pelle.quoteBits = convToBit(cel.pelle.quotes); + + delete player.celestials.teresa.quotes; + delete player.celestials.effarig.quotes; + delete player.celestials.enslaved.quotes; + delete player.celestials.v.quotes; + delete player.celestials.ra.quotes; + delete player.celestials.laitela.quotes; + delete player.celestials.pelle.quotes; + }, + player => { + if (player.celestials.pelle.rifts.famine) { + player.celestials.pelle.rifts.vacuum = { + ...player.celestials.pelle.rifts.famine, + fill: new Decimal(player.celestials.pelle.rifts.famine.fill) + }; + delete player.celestials.pelle.rifts.famine; + } + + if (player.celestials.pelle.rifts.pestilence) { + player.celestials.pelle.rifts.decay = { + ...player.celestials.pelle.rifts.pestilence, + fill: new Decimal(player.celestials.pelle.rifts.pestilence.fill) + }; + delete player.celestials.pelle.rifts.pestilence; + } + + if (player.celestials.pelle.rifts.war) { + player.celestials.pelle.rifts.recursion = { + ...player.celestials.pelle.rifts.war, + fill: new Decimal(player.celestials.pelle.rifts.war.fill) + }; + delete player.celestials.pelle.rifts.war; + } + + if (player.celestials.pelle.rifts.death) { + player.celestials.pelle.rifts.paradox = { + ...player.celestials.pelle.rifts.death, + fill: new Decimal(player.celestials.pelle.rifts.death.fill) + }; + delete player.celestials.pelle.rifts.death; + } + }, + player => { + delete player.newGame; + }, + GameStorage.migrations.moveTS33, + player => { + const toMove = ["antimatterDims", "infinityDims", "timeDims", "replicantiUpgrades", "dilationUpgrades", + "blackHolePower", "realityUpgrades", "imaginaryUpgrades"]; + for (const x of toMove) { + const all = player.auto[x]; + delete player.auto[x]; + player.auto[x] = { all, isActive: true }; + } + }, + player => { + player.celestials.ra.petWithRemembrance = player.celestials.ra.petWithRecollection; + delete player.celestials.ra.petWithRecollection; + }, + player => { + for (const key of Object.keys(player.reality.automator.scripts)) { + const lines = player.reality.automator.scripts[key].content.split("\n"); + for (let num = 0; num < lines.length; num++) { + let rawLine = lines[num]; + // TT command removed + rawLine = rawLine.replace(/^\s*tt.*$/ui, ""); + // Changes to "studies" commands + // For some reason `studies nowait load` would get caught by the following system without explicitly defining + // that "nowait load" should not be captured. Probably because it treats nowait as nonexisting and then sees + // that nowait is neither respec nor load. I tried consuming the nowait if it existed but that messed up the + // replace function so this is the best I've got for now + rawLine = rawLine.replace(/studies( nowait)? (?!respec|load|nowait respec|nowait load)(\S.+)$/ui, + "studies$1 purchase $2"); + rawLine = rawLine.replace(/studies( nowait)? load preset ([1-6])/ui, "studies$1 load id $2"); + rawLine = rawLine.replace(/studies( nowait)? load preset (\S+)/ui, "studies$1 load name $2"); + // Autobuyer mode change (this is a much older change which wasn't migrated at the time) + rawLine = rawLine.replace(/x current/ui, "x highest"); + // Variable definitions + const defineMatch = rawLine.match(/define (\S*)\s*=\s*(\S.*)$/ui); + if (defineMatch) { + player.reality.automator.constants[defineMatch[1]] = defineMatch[2]; + rawLine = ""; + } + lines[num] = rawLine; + } + player.reality.automator.scripts[key].content = lines.join("\n"); + } + + // Migrate IDs for all saves made during wave 3 testing, to prevent odd overwriting behavior on importing + const newScripts = {}; + const oldScriptKeys = Object.keys(player.reality.automator.scripts); + for (let newID = 1; newID <= oldScriptKeys.length; newID++) { + newScripts[newID] = player.reality.automator.scripts[oldScriptKeys[newID - 1]]; + newScripts[newID].id = newID; + } + player.reality.automator.scripts = newScripts; + }, + player => { + delete player.celestials.pelle.armageddonDuration; + delete player.celestials.pelle.maxAMThisArmageddon; + delete player.options.sidebarMinimized; + delete player.options.chart; + delete player.devMode; + }, + player => { + const swap1 = player.achievementBits[10] & 4; + const swap2 = player.achievementBits[11] & 8; + if (swap1) { + player.achievementBits[11] |= 8; + } else { + player.achievementBits[11] &= ~8; + } + if (swap2) { + player.achievementBits[10] |= 4; + } else { + player.achievementBits[10] &= ~4; + } + }, ], patch(player) { diff --git a/javascripts/core/storage/index.js b/javascripts/core/storage/index.js index 3c62e61f9..91b0685f1 100644 --- a/javascripts/core/storage/index.js +++ b/javascripts/core/storage/index.js @@ -1,6 +1,6 @@ -import "./migrations.js"; -import "./dev-migrations.js"; +import "./migrations"; +import "./dev-migrations"; -export * from "./serializer.js"; -export * from "./storage.js"; -export * from "./cloud-saving.js"; +export * from "./serializer"; +export * from "./storage"; +export * from "./cloud-saving"; diff --git a/javascripts/core/storage/migrations.js b/javascripts/core/storage/migrations.js index 131d0e5a3..457080c92 100644 --- a/javascripts/core/storage/migrations.js +++ b/javascripts/core/storage/migrations.js @@ -1,5 +1,5 @@ -import { GameStorage } from "./storage.js"; import { deepmergeAll } from "@/utility/deepmerge"; +import { GameStorage } from "./storage"; // WARNING: Don't use state accessors and functions from global scope here, that's not safe in long-term GameStorage.migrations = { @@ -146,8 +146,11 @@ GameStorage.migrations = { GameStorage.migrations.deleteDimboostBulk(player); GameStorage.migrations.deleteFloatingTextOption(player); GameStorage.migrations.refactorDoubleIPRebuyable(player); + GameStorage.migrations.infMultNameConversion(player); GameStorage.migrations.convertNews(player); GameStorage.migrations.etercreqConversion(player); + GameStorage.migrations.moveTS33(player); + GameStorage.migrations.addBestPrestigeCurrency(player); kong.migratePurchases(); } @@ -323,7 +326,6 @@ GameStorage.migrations = { if (player.challenges) { for (const fullID of player.challenges) { const parsed = parseChallengeName(fullID); - // eslint-disable-next-line no-bitwise player.challenge[parsed.type].completedBits |= 1 << parsed.id; } delete player.challenges; @@ -546,7 +548,7 @@ GameStorage.migrations = { for (let i = 0; i < 8; i++) { const old = player.autobuyers[i]; if (old % 1 === 0) continue; - const autobuyer = player.auto.antimatterDims[i]; + const autobuyer = player.auto.antimatterDims.all[i]; autobuyer.cost = old.cost; autobuyer.interval = old.interval; autobuyer.bulk = old.bulk; @@ -663,7 +665,6 @@ GameStorage.migrations = { const number = parseInt(groups[2], 10); if (!player.news.seen[type]) player.news.seen[type] = []; while (maskLength * player.news.seen[type].length < number) player.news.seen[type].push(0); - // eslint-disable-next-line no-bitwise player.news.seen[type][Math.floor(number / maskLength)] |= 1 << (number % maskLength); } @@ -698,35 +699,40 @@ GameStorage.migrations = { convertAchievementsToBits(player) { // Also switches achievement positions - const swaps = { "4,3": "6,4", "6,4": "7,7", "7,7": "4,3", "10,1": "11,7", "11,7": "10,1" }; - const convertAchievementArray = (newAchievements, oldAchievements) => { + // So far there've been three swaps + // (1) a three-way swap of zero deaths, 1 million is a lot, and antitables + // (2) a two-way swap of costco sells dimboosts now and 8 nobody got time for that + // (3) a two-way swap of long lasting relationship and eternities are the new infinity + const swaps = { "4,3": "6,4", "6,4": "7,7", "7,7": "4,3", + "10,1": "11,7", "11,7": "10,1", "11,3": "12,4", "12,4": "11,3" }; + const convertAchievementArray = (newAchievements, oldAchievements, isSecret) => { for (const oldId of oldAchievements) { let row = Math.floor(oldId / 10); let column = oldId % 10; - // eslint-disable-next-line no-bitwise - if ( - [row, column].join(",") in swaps) { + if (!isSecret && [row, column].join(",") in swaps) { [row, column] = swaps[[row, column].join(",")].split(","); } - // eslint-disable-next-line no-bitwise newAchievements[row - 1] |= (1 << (column - 1)); } // Handle the changed achievement "No DLC Required" correctly (otherwise saves could miss it). - if (player.infinityUpgrades.size >= 16 || player.eternities.gt(0) || player.realities > 0) { - // eslint-disable-next-line no-bitwise + if (!isSecret && (player.infinityUpgrades.size >= 16 || player.eternities.gt(0) || player.realities > 0)) { newAchievements[3] |= 1; } else { - // eslint-disable-next-line no-bitwise newAchievements[3] &= ~1; } + + // "Professional Bodybuilder" (s38) was changed and shouldn't be migrated + if (isSecret) { + newAchievements[2] &= ~128; + } }; player.achievementBits = Array.repeat(0, 15); - convertAchievementArray(player.achievementBits, player.achievements); + convertAchievementArray(player.achievementBits, player.achievements, false); delete player.achievements; player.secretAchievementBits = Array.repeat(0, 4); - convertAchievementArray(player.secretAchievementBits, player.secretAchievements); + convertAchievementArray(player.secretAchievementBits, player.secretAchievements, true); delete player.secretAchievements; }, @@ -835,10 +841,10 @@ GameStorage.migrations = { consolidateAuto(player) { for (let i = 0; i < 8; i++) { - player.auto.infinityDims[i].isActive = player.infDimBuyers[i]; + player.auto.infinityDims.all[i].isActive = player.infDimBuyers[i]; } for (let i = 0; i < 3; i++) { - player.auto.replicantiUpgrades[i].isActive = player.replicanti.auto[i]; + player.auto.replicantiUpgrades.all[i].isActive = player.replicanti.auto[i]; } player.auto.replicantiGalaxies.isActive = player.replicanti.galaxybuyer; player.auto.ipMultBuyer.isActive = player.infMultBuyer; @@ -905,11 +911,24 @@ GameStorage.migrations = { }, etercreqConversion(player) { - // eslint-disable-next-line no-bitwise if (player.etercreq) player.challenge.eternity.requirementBits |= 1 << player.etercreq; delete player.etercreq; }, + moveTS33(player) { + if (player.timestudy.studies.includes(33) && !player.timestudy.studies.includes(22)) { + player.timestudy.studies.splice(player.timestudy.studies.indexOf(33), 1); + player.timestudy.theorem = new Decimal(player.timestudy.theorem).plus(2); + } + }, + + addBestPrestigeCurrency(player) { + player.records.thisReality.maxEP = player.eternityPoints; + player.records.bestReality.bestEP = player.eternityPoints; + player.records.thisEternity.maxIP = player.infinityPoints; + player.records.thisReality.maxIP = player.infinityPoints; + }, + prePatch(saveData) { // Initialize all possibly undefined properties that were not present in // previous versions and which could be overwritten by deepmerge diff --git a/javascripts/core/storage/progress-checker.js b/javascripts/core/storage/progress-checker.js index aa7a0fe95..1a7847945 100644 --- a/javascripts/core/storage/progress-checker.js +++ b/javascripts/core/storage/progress-checker.js @@ -1,72 +1,55 @@ -// These "progress stages" are roughly defined in a way to separate different parts of the game where different -// resources are the main indicator of progress. They aren't necessarily equally spaced in time. -const PROGRESS_STAGE = { - PRE_INFINITY: 0, - EARLY_INFINITY: 1, - INFINITY: 2, - EARLY_ETERNITY: 3, - ETERNITY: 4, - EARLY_DILATION: 5, - LATE_ETERNITY: 6, - EARLY_REALITY: 7, - REALITY: 8, - IMAGINARY_MACHINES: 9, -}; +import { GameMechanicState } from "../game-mechanics/index"; + +class GameProgressState extends GameMechanicState { + get id() { + return this.config.id; + } + + get name() { + return this.config.name; + } + + get suggestedResource() { + return this.config.suggestedResource; + } +} + +export const GameProgress = GameProgressState.createAccessor(GameDatabase.progressStages); +GameProgress.all = GameDatabase.progressStages; + +class CatchupResource extends GameMechanicState { + get requiredStage() { + return this.config.requiredStage; + } + + get name() { + return this.config.name; + } + + get description() { + return typeof this.config.description === "function" ? this.config.description() : this.config.description; + } +} + +export const CatchupResources = mapGameDataToObject( + GameDatabase.catchupResources, + config => new CatchupResource(config) +); export const ProgressChecker = { - // Returns an enum in an ordered list defined by certain progress breakpoints in the game - // Note that cloud saves will have Decimal props stored as Strings which need to be converted getProgressStage(save) { - if (save.reality.iMCap > 0) return PROGRESS_STAGE.IMAGINARY_MACHINES; - if (save.realities > 50) return PROGRESS_STAGE.REALITY; - if (save.realities > 0) return PROGRESS_STAGE.EARLY_REALITY; - const dilatedTime = new Decimal(save.dilation.dilatedTime); - if (dilatedTime.gt(1e15)) return PROGRESS_STAGE.LATE_ETERNITY; - if (dilatedTime.gt(0)) return PROGRESS_STAGE.EARLY_DILATION; - const eternities = new Decimal(save.eternities); - if (eternities.gt(1000)) return PROGRESS_STAGE.ETERNITY; - if (eternities.gt(0)) return PROGRESS_STAGE.EARLY_ETERNITY; - const infinities = new Decimal(save.infinities); - if (infinities.gt(1000)) return PROGRESS_STAGE.INFINITY; - if (infinities.gt(0)) return PROGRESS_STAGE.EARLY_INFINITY; - return PROGRESS_STAGE.PRE_INFINITY; - }, - - // Returns a Number scaled roughly 0-1000 which can be used to compare progress within the same stage. Higher values - // don't necessarily indicate strictly farther progress when they're close to each other, but the general trend is - // that 1000 will be approximately "equal to" 0 on the next stage - // The new Decimal followed by toNumber is effectively using break_infinity to parse a Decimal String for us - getProgressWithinStage(save) { - switch (this.getProgressStage(save)) { - case PROGRESS_STAGE.PRE_INFINITY: - return (330 * save.galaxies) + (20 * save.dimensionBoosts) + (new Decimal(save.antimatter).log10() / 30); - case PROGRESS_STAGE.EARLY_INFINITY: - return new Decimal(save.infinities).toNumber(); - case PROGRESS_STAGE.INFINITY: - return 1000 * Math.sqrt(new Decimal(save.infinityPoints).log10() / 310); - case PROGRESS_STAGE.EARLY_ETERNITY: - return new Decimal(save.eternities).toNumber(); - case PROGRESS_STAGE.ETERNITY: - return 1000 * Math.sqrt(new Decimal(save.eternityPoints).log10() / 1300); - case PROGRESS_STAGE.EARLY_DILATION: - return new Decimal(save.dilation.dilatedTime).log10() / 0.015; - case PROGRESS_STAGE.LATE_ETERNITY: - return 1000 * Math.sqrt((new Decimal(save.eternityPoints).log10() - 1300) / 6700); - case PROGRESS_STAGE.EARLY_REALITY: - return 20 * save.realities; - case PROGRESS_STAGE.REALITY: - return 1000 * Math.sqrt(new Decimal(save.reality.realityMachines).log10() / 1000); - case PROGRESS_STAGE.IMAGINARY_MACHINES: - return 50 * Math.log10(save.reality.iMCap); - default: - throw Error("Unrecognized progress stage in getProgressWithinStage"); + const db = GameProgress.all; + for (let stage = db.length - 1; stage >= 0; stage--) { + if (db[stage].hasReached(save)) return db[stage]; } + throw Error("No valid progress stage found"); }, - // Note: Don't use this function as an absolute indicator of progress as it's unreliable when numbers are close + // Returns a value corresponding to keys in PROGRESS_STAGE, with a rough interpolation between stages getCompositeProgress(save) { if (!save) return 0; - return this.getProgressStage(save) + this.getProgressWithinStage(save) / 1000; + const stage = this.getProgressStage(save); + return stage.id + Math.clampMax(stage.subProgressValue(save), 1); }, // Returns -1 or 1 when one save is very likely to be farther than the other, otherwise returns 0 if they're close @@ -77,7 +60,7 @@ export const ProgressChecker = { return 0; }, - // Returns -1 or 1 based on which save is older. Return 0 if one is undefined, will be handled upstream + // Returns -1 or 1 based on which save is older. Returns 0 if one is undefined, will be handled upstream compareSaveTimes(first, second) { if (!first || !second) return 0; const timeDifference = first.records.realTimePlayed - second.records.realTimePlayed; diff --git a/javascripts/core/storage/serializer.js b/javascripts/core/storage/serializer.js index d80a0dfd8..eea9eed96 100644 --- a/javascripts/core/storage/serializer.js +++ b/javascripts/core/storage/serializer.js @@ -1,4 +1,6 @@ +/* eslint-disable import/extensions */ import pako from "pako/dist/pako.esm.mjs"; +/* eslint-enable import/extensions */ export const GameSaveSerializer = { serialize(save) { @@ -39,12 +41,14 @@ export const GameSaveSerializer = { // also require changing some other code slightly, particularly decode). startingString: { savefile: "AntimatterDimensionsSavefileFormat", - "automator script": "AntimatterDimensionsAutomatorScriptFormat" + "automator script": "AntimatterDimensionsAutomatorScriptFormat", + "automator data": "AntimatterDimensionsAutomatorDataFormat" }, // The ending strings aren't as verbose so that we can save a little space. endingString: { savefile: "EndOfSavefile", "automator script": "EndOfAutomatorScript", + "automator data": "EndOfAutomatorData" }, // This should always be three characters long, and should ideally go AAA, AAB, AAC, etc. // so that we can do inequality tests on it to compare versions (though skipping a version diff --git a/javascripts/core/storage/storage.js b/javascripts/core/storage/storage.js index 1f6de1d2b..735f1304a 100644 --- a/javascripts/core/storage/storage.js +++ b/javascripts/core/storage/storage.js @@ -1,4 +1,5 @@ import * as ADNotations from "@antimatter-dimensions/notations"; + import { deepmergeAll } from "@/utility/deepmerge"; export const GameStorage = { @@ -51,7 +52,7 @@ export const GameStorage = { this.currentSlot = slot; // Save current slot to make sure no changes are lost this.save(true); - this.loadPlayerObject(this.saves[slot]); + this.loadPlayerObject(this.saves[slot] ?? Player.defaultStart); Tabs.all.find(t => t.id === player.options.lastOpenTab).show(true); GameUI.notify.info("Game loaded"); SteamFunctions.BackfillAchievements() @@ -63,18 +64,27 @@ export const GameStorage = { } const player = GameSaveSerializer.deserialize(saveData); if (this.checkPlayerObject(player) !== "") { - Modal.message.show("Could not load the save"); + Modal.message.show("Could not load the save (format unrecognized or invalid)."); return; } Modal.hideAll(); + Quote.clearAll(); + AutomatorBackend.clearEditor(); this.loadPlayerObject(player, overrideLastUpdate); if (player.speedrun?.isActive) Speedrun.setSegmented(true); this.save(true); + + // This is to fix a very specific exploit: When the game is ending, some tabs get hidden + // The options tab is the first one of those, which makes the player redirect to the Pelle tab + // You can doom your reality even if you haven't unlocked infinity yet if you import while the Pelle tab + // is showing + Tab.options.subtabs[0].show(); GameUI.notify.info("Game imported"); SteamFunctions.BackfillAchievements() }, importAsFile() { + if (GameEnd.creditsEverClosed) return; const reader = new FileReader(); const text = reader.readAsText(file); this.import(text); @@ -135,7 +145,12 @@ export const GameStorage = { }, save(silent = false, manual = false) { - if (Pelle.endState >= 4.5) return; + if (GameEnd.endState >= END_STATE_MARKERS.SAVE_DISABLED && !GameEnd.removeAdditionalEnd) return; + if (GameEnd.endState >= END_STATE_MARKERS.INTERACTIVITY_DISABLED) { + // Fade-out starts at 2.5 + GameUI.notify.error("There is nothing left to save"); + return; + } if (GlyphSelection.active || ui.$viewModel.modal.progressBar !== undefined) return; this.lastSaveTime = Date.now(); GameIntervals.save.restart(); @@ -160,21 +175,25 @@ export const GameStorage = { exportAsFile() { player.options.exportedFileCount++; this.save(true); + const saveFileName = player.options.saveFileName ? ` - ${player.options.saveFileName},` : ""; const dateObj = new Date(); const y = dateObj.getFullYear(); const m = dateObj.getMonth() + 1; const d = dateObj.getDate(); const segmented = player.speedrun.isSegmented; Speedrun.setSegmented(true); - download(`AD Save ${GameStorage.currentSlot + 1} #${player.options.exportedFileCount} (${y}-${m}-${d}).txt`, - GameSaveSerializer.serialize(player)); + download( + `AD Save, Slot ${GameStorage.currentSlot + 1}${saveFileName} #${player.options.exportedFileCount} \ +(${y}-${m}-${d}).txt`, GameSaveSerializer.serialize(player)); Speedrun.setSegmented(segmented); GameUI.notify.info("Successfully downloaded current save file to your computer"); }, hardReset() { + const IAP = JSON.parse(JSON.stringify(player.IAP)); this.loadPlayerObject(Player.defaultStart); - this.save(); + player.IAP = IAP; + this.save(true); Tab.dimensions.antimatter.show(); }, @@ -218,6 +237,7 @@ export const GameStorage = { checkPerkValidity(); V.updateTotalRunUnlocks(); Enslaved.boostReality = false; + GameEnd.additionalEnd = 0; Theme.set(player.options.theme); Notations.find(player.options.notation).setAsCurrent(true); ADNotations.Settings.exponentCommas.show = player.options.commas; @@ -229,8 +249,9 @@ export const GameStorage = { if (overrideLastUpdate) { player.lastUpdate = overrideLastUpdate; } + const rawDiff = Date.now() - player.lastUpdate; if (player.options.offlineProgress && !Speedrun.isPausedAtStart()) { - let diff = Date.now() - player.lastUpdate; + let diff = rawDiff; player.speedrun.offlineTimeUsed += diff; if (diff > 5 * 60 * 1000 && player.celestials.enslaved.autoStoreReal) { diff = Enslaved.autoStoreRealTime(diff); @@ -252,6 +273,15 @@ export const GameStorage = { player.lastUpdate = Date.now(); this.postLoadStuff(); } + + // 2-week threshold for showing the catchup modal. We want to show this even if offline progress is disabled + // because its presence and usefulness is tied to what the player experiences, not the game. setTimeout seems to be + // the only way to get this to display, as it won't display even if called after init() entirely nor is it getting + // actively hidden by Modal.hideAll(), so delaying it asynchronously gets past whatever is causing it to not appear. + // Delay time is relatively long to make it more likely to work on much slower computers. + if (rawDiff > 1000 * 86400 * 14) { + setTimeout(() => Modal.catchup.show(rawDiff), 5000); + } }, postLoadStuff() { // This is called from simulateTime, if that's called; otherwise, it gets called diff --git a/javascripts/core/tickspeed.js b/javascripts/core/tickspeed.js index 666ef7e63..88cb55d48 100644 --- a/javascripts/core/tickspeed.js +++ b/javascripts/core/tickspeed.js @@ -1,4 +1,4 @@ -import { DC } from "./constants.js"; +import { DC } from "./constants"; export function getTickSpeedMultiplier() { if (InfinityChallenge(3).isRunning) return DC.D1; @@ -40,7 +40,7 @@ export function getTickSpeedMultiplier() { Achievement(178), InfinityChallenge(5).reward, PelleUpgrade.galaxyPower, - PelleRifts.pestilence.milestones[1] + PelleRifts.decay.milestones[1] ); if (Pelle.isDoomed) galaxies *= 0.5; @@ -60,7 +60,7 @@ export function getTickSpeedMultiplier() { Achievement(178), InfinityChallenge(5).reward, PelleUpgrade.galaxyPower, - PelleRifts.pestilence.milestones[1] + PelleRifts.decay.milestones[1] ); galaxies *= getAdjustedGlyphEffect("cursedgalaxies"); galaxies *= getAdjustedGlyphEffect("realitygalaxies"); diff --git a/javascripts/core/time-studies/dilation-time-study.js b/javascripts/core/time-studies/dilation-time-study.js index 179080935..f4bf5c793 100644 --- a/javascripts/core/time-studies/dilation-time-study.js +++ b/javascripts/core/time-studies/dilation-time-study.js @@ -1,5 +1,5 @@ -import { TimeStudyState } from "./time-studies.js"; -import { TimeStudy } from "./normal-time-study.js"; +import { TimeStudy } from "./normal-time-study"; +import { TimeStudyState } from "./time-studies"; export class DilationTimeStudyState extends TimeStudyState { constructor(config) { @@ -33,15 +33,15 @@ export class DilationTimeStudyState extends TimeStudyState { if (!quiet) { Tab.eternity.dilation.show(); } - if (Perk.autounlockDilation1.isBought && !Pelle.isDoomed) { + if (Perk.autounlockDilation1.canBeApplied) { for (const id of [4, 5, 6]) player.dilation.upgrades.add(id); } - if (Perk.autounlockDilation2.isBought && !Pelle.isDoomed) { + if (Perk.autounlockDilation2.canBeApplied) { for (const id of [7, 8, 9]) player.dilation.upgrades.add(id); } if (!Pelle.isDoomed) Currency.tachyonParticles.bumpTo(Perk.startTP.effectOrDefault(0)); - if (Ra.has(RA_UNLOCKS.START_TP) && !isInCelestialReality() && !Pelle.isDoomed) { - Currency.tachyonParticles.bumpTo(getTP(RA_UNLOCKS.START_TP.effect())); + if (Ra.unlocks.unlockDilationStartingTP.canBeApplied && !isInCelestialReality() && !Pelle.isDoomed) { + Currency.tachyonParticles.bumpTo(getTP(Ra.unlocks.unlockDilationStartingTP.effectOrDefault(0))); } TabNotification.dilationAfterUnlock.tryTrigger(); } @@ -51,7 +51,7 @@ export class DilationTimeStudyState extends TimeStudyState { Modal.message.show(`Reality Machine gain for your first Reality is reduced above ${format("1e6000")} Eternity Points and capped at ${format("1e8000")} Eternity Points. This is due to balance changes made in the Reality update which affect the difficulty of reaching those amounts, such as the increased Time Dimension cost - scaling above ${format("1e6000")}.`); + scaling above ${format("1e6000")}.`, {}, 3); EventHub.dispatch(GAME_EVENT.REALITY_FIRST_UNLOCKED); } if (!Perk.autounlockReality.isBought) Tab.reality.glyphs.show(); diff --git a/javascripts/core/time-studies/ec-time-study.js b/javascripts/core/time-studies/ec-time-study.js index 978c96b31..c251135c0 100644 --- a/javascripts/core/time-studies/ec-time-study.js +++ b/javascripts/core/time-studies/ec-time-study.js @@ -1,5 +1,5 @@ -import { TimeStudyState } from "./time-studies.js"; -import { TimeStudy } from "./normal-time-study.js"; +import { TimeStudy } from "./normal-time-study"; +import { TimeStudyState } from "./time-studies"; export class ECTimeStudyState extends TimeStudyState { constructor(config) { @@ -12,6 +12,7 @@ export class ECTimeStudyState extends TimeStudyState { } purchase(auto) { + if (GameEnd.creditsEverClosed) return false; const clickTime = Date.now(); if (this.isBought && player.challenge.eternity.current === 0 && !auto) { @@ -33,7 +34,6 @@ export class ECTimeStudyState extends TimeStudyState { if (!auto) { Tab.challenges.eternity.show(); } - // eslint-disable-next-line no-bitwise player.challenge.eternity.requirementBits |= 1 << this.id; Currency.timeTheorems.subtract(this.cost); TimeStudyTree.commitToGameState([TimeStudy.eternityChallenge(this.id)]); @@ -117,7 +117,7 @@ export class ECTimeStudyState extends TimeStudyState { } get wasRequirementPreviouslyMet() { - // eslint-disable-next-line no-bitwise + if (this.id === 11 || this.id === 12) return false; return (player.challenge.eternity.requirementBits & (1 << this.id)) !== 0; } diff --git a/javascripts/core/time-studies/index.js b/javascripts/core/time-studies/index.js index acaf2fac4..143bee257 100644 --- a/javascripts/core/time-studies/index.js +++ b/javascripts/core/time-studies/index.js @@ -1,6 +1,6 @@ -export * from "./time-studies.js"; -export * from "./time-study-tree.js"; -export * from "./normal-time-study.js"; -export * from "./ec-time-study.js"; -export * from "./dilation-time-study.js"; -export * from "./time-study-connections.js"; +export * from "./time-studies"; +export * from "./time-study-tree"; +export * from "./normal-time-study"; +export * from "./ec-time-study"; +export * from "./dilation-time-study"; +export * from "./time-study-connections"; diff --git a/javascripts/core/time-studies/normal-time-study.js b/javascripts/core/time-studies/normal-time-study.js index c69f3831f..6905c4cba 100644 --- a/javascripts/core/time-studies/normal-time-study.js +++ b/javascripts/core/time-studies/normal-time-study.js @@ -1,4 +1,4 @@ -import { TimeStudyState } from "./time-studies.js"; +import { TimeStudyState } from "./time-studies"; export const NormalTimeStudies = {}; @@ -23,6 +23,10 @@ export class NormalTimeStudyState extends TimeStudyState { this._path = path?.path ?? TIME_STUDY_PATH.NONE; } + get isUnlocked() { + return this.config.unlocked?.() ?? true; + } + get isTriad() { return this.id > 300; } @@ -73,6 +77,7 @@ export class NormalTimeStudyState extends TimeStudyState { purchase() { if (this.isBought || !this.isAffordable || !this.canBeBought) return false; + if (GameEnd.creditsEverClosed) return false; if (this.costsST()) player.celestials.v.STSpent += this.STCost; player.timestudy.studies.push(this.id); player.requirementChecks.reality.maxStudies = Math.clampMin(player.requirementChecks.reality.maxStudies, diff --git a/javascripts/core/time-studies/time-studies.js b/javascripts/core/time-studies/time-studies.js index 45955c045..16c7d2bd4 100644 --- a/javascripts/core/time-studies/time-studies.js +++ b/javascripts/core/time-studies/time-studies.js @@ -1,4 +1,14 @@ -import { GameMechanicState } from "../game-mechanics/index.js"; +import { GameMechanicState } from "../game-mechanics/index"; + +function showSecondPreferredWarning(currTree) { + const canPickSecond = currTree.allowedDimPathCount === 2 && currTree.currDimPathCount < 2; + // Show a warning if the player can choose the second preferred dimension path and hasn't yet done so. + if (canPickSecond && TimeStudy.preferredPaths.dimension.path.length < 2) { + GameUI.notify.error("You haven't selected a second preferred Dimension path."); + return true; + } + return false; +} // This is only ever called from manual player actions, which means we can immediately commit them to the game state // eslint-disable-next-line complexity @@ -24,8 +34,7 @@ export function buyStudiesUntil(id, ec = -1) { // Priority for behavior when buying in the Dimension split; we follow only the first applicable entry below: // - If we're buying a study within the split, we first buy just the requested path up to the requested study. - // If we still have additional available paths at this point, we also buy others in order specified first by the - // player's chosen priority and then numerically (stops buying) + // (stops buying) // - If we want to buy EC11 or EC12 we only buy the required dimension path unless we have the EC requirement perk // (continues onward) // - If we can't buy any additional paths or have 3 paths available, we attempt to buy everything here, prioritizing @@ -33,15 +42,9 @@ export function buyStudiesUntil(id, ec = -1) { // (continues onward) // - If the player has a preferred path, we attempt to buy it (continues onward) // - If the player doesn't have a preferred path, we say so and do nothing (stops buying) + // - Otherwise we do nothing (stops buying) if (id < 111) { studyArray.push(...NormalTimeStudies.paths[requestedPath].filter(s => (s <= id))); - // The purchasing logic is doing the heavy lifting here; studies can't be double-bought, nor can they be bought - // if we don't have another available path - const pathBuyOrder = TimeStudy.preferredPaths.dimension.path - .concat([TIME_STUDY_PATH.ANTIMATTER_DIM, TIME_STUDY_PATH.INFINITY_DIM, TIME_STUDY_PATH.TIME_DIM]); - for (const path of pathBuyOrder) { - studyArray.push(...NormalTimeStudies.paths[path].filter(s => s <= lastInPrevRow)); - } return studyArray; } @@ -54,13 +57,16 @@ export function buyStudiesUntil(id, ec = -1) { studyArray.push(...range(71, 103)); } else if (TimeStudy.preferredPaths.dimension.path.length > 0) { studyArray.push(...TimeStudy.preferredPaths.dimension.studies); - } else { + } else if (currTree.currDimPathCount === 0) { GameUI.notify.error("You haven't selected a preferred Dimension path."); return studyArray; } // Explicitly purchase 111 here if it's included and stop if applicable, as it isn't covered by logic in either split. if (id >= 111) studyArray.push(111); + + const secondPreferredWarningShown = showSecondPreferredWarning(currTree); + if (id < 121) return studyArray; // Priority for behavior when buying in the Pace split; we follow only the first applicable entry below. In contrast @@ -105,14 +111,17 @@ export function buyStudiesUntil(id, ec = -1) { TimeStudyTree.commitToGameState(studyArray); studyArray = []; - // Buy the second preferred dimension path if we have one, otherwise show a warning if - // the player can choose the second preferred dimension path and hasn't yet done so. - if (TimeStudy.preferredPaths.dimension.path.length > 1) { - studyArray.push(...TimeStudy.preferredPaths.dimension.studies.filter(s => (s <= id))); - } else if (GameCache.currentStudyTree.value.allowedDimPathCount === 2) { - GameUI.notify.error("You haven't selected a second preferred Dimension path."); + // Buy the second preferred dimension path if we have one + if (TimeStudy.preferredPaths.dimension.path.length > 0) { + studyArray.push(...TimeStudy.preferredPaths.dimension.studies); + // We need to commit the dimension paths to the game state in order + // to know if we should display the second preferred path warning. + TimeStudyTree.commitToGameState(studyArray); + studyArray = []; } + if (!secondPreferredWarningShown) showSecondPreferredWarning(GameCache.currentStudyTree.value); + studyArray.push(...range(211, Math.min(lastInPrevRow, 214))); // If the user clicked on a study in rows 19-22, we've tried to buy up to the previous @@ -131,9 +140,6 @@ export function respecTimeStudies(auto) { for (const study of TimeStudy.boughtNormalTS()) { study.refund(); } - if (player.timestudy.studies.length === 0) { - SecretAchievement(34).unlock(); - } player.timestudy.studies = []; GameCache.timeStudies.invalidate(); player.celestials.v.STSpent = 0; @@ -160,7 +166,7 @@ export class TimeStudyState extends GameMechanicState { get STCost() { const base = this.config.STCost; - return V.has(V_UNLOCKS.RA_UNLOCK) + return VUnlocks.raUnlock.canBeApplied ? base - 2 : base; } diff --git a/javascripts/core/time-studies/time-study-connections.js b/javascripts/core/time-studies/time-study-connections.js index 15d3712d8..f0271a3f1 100644 --- a/javascripts/core/time-studies/time-study-connections.js +++ b/javascripts/core/time-studies/time-study-connections.js @@ -1,4 +1,4 @@ -import { TimeStudy } from "./normal-time-study.js"; +import { TimeStudy } from "./normal-time-study"; export class TimeStudyConnection { constructor(from, to, override) { @@ -35,7 +35,7 @@ TimeStudy.allConnections = (function() { [TS(11), TS(22)], [TS(21), TS(31)], - [TS(21), TS(33)], + [TS(22), TS(33)], [TS(22), TS(32)], [TS(31), TS(41)], diff --git a/javascripts/core/time-studies/time-study-tree.js b/javascripts/core/time-studies/time-study-tree.js index 39b58d742..96e08d7e8 100644 --- a/javascripts/core/time-studies/time-study-tree.js +++ b/javascripts/core/time-studies/time-study-tree.js @@ -74,10 +74,10 @@ export class TimeStudyTree { // THIS METHOD HAS LASTING CONSEQUENCES ON THE GAME STATE. STUDIES WILL ACTUALLY BE PURCHASED IF POSSIBLE. // This method attempts to take the parameter array and purchase all the studies specified, using the current game // state to determine if they are affordable. Input array may be either an id array or a TimeStudyState array - static commitToGameState(studyArray) { + static commitToGameState(studyArray, auto = true) { for (const item of studyArray) { const study = typeof item === "number" ? TimeStudy(item) : item; - if (study && !study.isBought) study.purchase(true); + if (study && !study.isBought) study.purchase(auto); } GameCache.currentStudyTree.invalidate(); } @@ -93,6 +93,9 @@ export class TimeStudyTree { ["idle", [123, 133, 143]], ["light", [221, 223, 225, 227, 231, 233]], ["dark", [222, 224, 226, 228, 232, 234]], + ...(Ra.unlocks.unlockHardV.canBeApplied + ? [["triad", [301, 302, 303, 304].slice(0, Ra.unlocks.unlockHardV.effectOrDefault(0))]] + : []) ]); } @@ -102,24 +105,15 @@ export class TimeStudyTree { this.sets.forEach((ids, name) => (internal = internal.replace(name, ids.join()))); return internal .replace(/[|,]$/u, "") - .replaceAll(" ", ""); + .replaceAll(" ", "") + // Allows 11,,21 to be parsed as 11,21 and 11,|1 to be parsed as 11|1 + .replace(/,{2,}/gu, ",") + .replace(/,\|/gu, "|"); } static formatStudyList(input) { - let internal = input.toLowerCase().replaceAll(" ", ""); - // \\b means 0-width word boundry, meaning "target = 11" doesnt match 111 - const testRegex = target => new RegExp(`\\b${target}\\b,?`, "gu"); - // If the studylist has all IDs, replace the first instance with the shorthand, then remove the rest - this.sets.forEach((ids, name) => { - const hasAllIds = ids.every(x => testRegex(x).test(internal)); - if (hasAllIds) { - internal = internal.replace(testRegex(ids[0]), `${name},`); - for (const i of ids) { - internal = internal.replace(testRegex(i), ""); - } - } - }); - return internal.replaceAll(",", ", "); + const internal = input.toLowerCase().replaceAll(" ", ""); + return internal.replaceAll(",", ", ").replace("|", " | "); } // This reads off all the studies in the import string and splits them into invalid and valid study IDs. We hold on @@ -127,19 +121,22 @@ export class TimeStudyTree { parseStudyImport(input) { const studyDB = GameDatabase.eternity.timeStudies.normal.map(s => s.id); const output = []; - const studyCluster = TimeStudyTree.truncateInput(input).split("|")[0].split(","); - for (const studyRange of studyCluster) { - const studyRangeSplit = studyRange.split("-"); - const studyArray = studyRangeSplit[1] - ? this.studyRangeToArray(studyRangeSplit[0], studyRangeSplit[1]) - : studyRangeSplit; - for (const study of studyArray) { - if (studyDB.includes(parseInt(study, 10))) { - const tsObject = TimeStudy(study); - this.selectedStudies.push(tsObject); - output.push(tsObject); - } else { - this.invalidStudies.push(study); + const studiesString = TimeStudyTree.truncateInput(input).split("|")[0]; + if (studiesString.length) { + const studyCluster = studiesString.split(","); + for (const studyRange of studyCluster) { + const studyRangeSplit = studyRange.split("-"); + const studyArray = studyRangeSplit[1] + ? this.studyRangeToArray(studyRangeSplit[0], studyRangeSplit[1]) + : studyRangeSplit; + for (const study of studyArray) { + if (studyDB.includes(parseInt(study, 10))) { + const tsObject = TimeStudy(study); + this.selectedStudies.push(tsObject); + output.push(tsObject); + } else { + this.invalidStudies.push(study); + } } } } @@ -237,7 +234,7 @@ export class TimeStudyTree { // Buys the specified study; no requirement verification beyond cost, use hasRequirements() to verify proper structure buySingleStudy(study, checkCosts) { const config = study.config; - const stDiscount = V.has(V_UNLOCKS.RA_UNLOCK) ? 2 : 0; + const stDiscount = VUnlocks.raUnlock.effectOrDefault(0); const stNeeded = config.STCost && config.requiresST.some(s => this.purchasedStudies.includes(TimeStudy(s))) ? Math.clampMin(config.STCost - stDiscount, 0) : 0; diff --git a/javascripts/core/time-theorems.js b/javascripts/core/time-theorems.js index 7a1b0c264..b0a7e3f77 100644 --- a/javascripts/core/time-theorems.js +++ b/javascripts/core/time-theorems.js @@ -1,4 +1,4 @@ -import { DC } from "./constants.js"; +import { DC } from "./constants"; /** * @abstract @@ -34,7 +34,7 @@ export class TimeTheoremPurchaseType { get costIncrement() { throw new NotImplementedError(); } get bulkPossible() { - if (Perk.ttFree.isBought) { + if (Perk.ttFree.canBeApplied) { return Math.floor(this.currency.value.divide(this.cost).log10() / this.costIncrement.log10() + 1); } return Decimal.affordGeometricSeries(this.currency.value, this.cost, this.costIncrement, 0).toNumber(); @@ -50,7 +50,7 @@ export class TimeTheoremPurchaseType { if (!this.canAfford) return false; let purchased = false; const amount = this.bulkPossible; - const buyFn = cost => (Perk.ttFree.isBought ? this.currency.gte(cost) : this.currency.purchase(cost)); + const buyFn = cost => (Perk.ttFree.canBeApplied ? this.currency.gte(cost) : this.currency.purchase(cost)); // This will sometimes buy one too few for EP, so we just have to buy 1 after. if (bulk && buyFn(this.bulkCost(amount))) { Currency.timeTheorems.add(amount); @@ -104,7 +104,7 @@ TimeTheoremPurchaseType.ep = new class extends TimeTheoremPurchaseType { get costIncrement() { return DC.D2; } bulkCost(amount) { - if (Perk.ttFree.isBought) return this.cost.times(this.costIncrement.pow(amount - 1)); + if (Perk.ttFree.canBeApplied) return this.cost.times(this.costIncrement.pow(amount - 1)); return this.costIncrement.pow(amount + this.amount).subtract(this.cost); } }(); @@ -112,7 +112,8 @@ TimeTheoremPurchaseType.ep = new class extends TimeTheoremPurchaseType { export const TimeTheorems = { checkForBuying(auto) { if (PlayerProgress.realityUnlocked() || TimeDimension(1).bought) return true; - if (!auto) Modal.message.show("You need to buy at least 1 Time Dimension before you can purchase Time Theorems."); + if (!auto) Modal.message.show(`You need to buy at least ${formatInt(1)} Time Dimension before you can purchase + Time Theorems.`, { closeEvent: GAME_EVENT.REALITY_RESET_AFTER }); return false; }, @@ -153,8 +154,7 @@ export const TimeTheorems = { if (ecStudy !== undefined) { totalCost += ecStudy.cost; } - // Secret time study - if (Enslaved.isRunning && player.secretUnlocks.viewSecretTS) totalCost -= 100; + if (Enslaved.isRunning && player.celestials.enslaved.hasSecretStudy) totalCost -= 100; return totalCost; } }; diff --git a/javascripts/core/timespan.js b/javascripts/core/timespan.js index 2cebb8378..d488ac45a 100644 --- a/javascripts/core/timespan.js +++ b/javascripts/core/timespan.js @@ -233,6 +233,10 @@ window.TimeSpan = class TimeSpan { * @returns {String} */ toStringShort(useHMS = true) { + // Probably not worth the trouble of importing the isEND function from formatting since this accomplishes the same + // thing; we do however need this to prevent strings like "02:32" from showing up though + if (format(0) === "END") return "END"; + const totalSeconds = this.totalSeconds; if (totalSeconds > 5e-7 && totalSeconds < 1e-3) { // This conditional happens when when the time is less than 1 millisecond diff --git a/javascripts/core/tutorial.js b/javascripts/core/tutorial.js index 47b7a38e9..f9afa6848 100644 --- a/javascripts/core/tutorial.js +++ b/javascripts/core/tutorial.js @@ -1,9 +1,10 @@ export const TUTORIAL_STATE = { DIM1: 0, DIM2: 1, - DIMBOOST: 2, - GALAXY: 3, - AUTOMATOR: 4 + TICKSPEED: 2, + DIMBOOST: 3, + GALAXY: 4, + AUTOMATOR: 5 }; // Tutorial has two ways of moving on, either by Tutorial.moveOn() or by having it's condition be true @@ -18,6 +19,10 @@ const tutorialStates = [ id: TUTORIAL_STATE.DIM2, condition: () => Currency.antimatter.gte(100) }, + { + id: TUTORIAL_STATE.TICKSPEED, + condition: () => AntimatterDimension(2).bought > 0 + }, { id: TUTORIAL_STATE.DIMBOOST, condition: () => AntimatterDimension(4).amount.gte(20) @@ -34,11 +39,8 @@ const tutorialStates = [ export const Tutorial = { - // Class to be given to glowing components - glowingClass(atState, conditional = true) { - return { - "tutorial--glow": ui.view.tutorialState === atState && conditional && ui.view.tutorialActive - }; + isActive(atState) { + return ui.view.tutorialState === atState && ui.view.tutorialActive; }, // Turns off the visual effect diff --git a/javascripts/core/ui/tab-notifications.js b/javascripts/core/ui/tab-notifications.js index a4555759d..605c177bd 100644 --- a/javascripts/core/ui/tab-notifications.js +++ b/javascripts/core/ui/tab-notifications.js @@ -9,7 +9,6 @@ class TabNotificationState { } get triggered() { - // eslint-disable-next-line no-bitwise return player.triggeredTabNotificationBits & (1 << this.config.id); } @@ -17,7 +16,6 @@ class TabNotificationState { if (!this.config.condition() || this.triggered) return; this.config.tabsToHighLight.map(t => t.parent + t.tab) .forEach(tab => player.tabNotifications.add(tab)); - // eslint-disable-next-line no-bitwise player.triggeredTabNotificationBits |= 1 << this.config.id; // Force all tabs and subtabs of this notification to be unhidden @@ -28,21 +26,16 @@ class TabNotificationState { subtab.unhideTab(); } } + + // In some cases we want to clear a trigger via an event that isn't tab-clicking, in order to show it again + clearTrigger() { + player.triggeredTabNotificationBits &= -1 - (1 << this.config.id); + this.config.tabsToHighLight.map(t => t.parent + t.tab) + .forEach(tab => player.tabNotifications.delete(tab)); + } } -export const TabNotification = (function() { - const db = GameDatabase.tabNotifications; - return { - firstInfinity: new TabNotificationState(db.firstInfinity), - IDUnlock: new TabNotificationState(db.ICUnlock), - ICUnlock: new TabNotificationState(db.ICUnlock), - breakInfinity: new TabNotificationState(db.breakInfinity), - firstEternity: new TabNotificationState(db.firstEternity), - dilationAfterUnlock: new TabNotificationState(db.dilationAfterUnlock), - realityUnlock: new TabNotificationState(db.realityUnlock), - blackHoleUnlock: new TabNotificationState(db.blackHoleUnlock), - automatorUnlock: new TabNotificationState(db.automatorUnlock), - teresaUnlock: new TabNotificationState(db.teresaUnlock), - alchemyUnlock: new TabNotificationState(db.alchemyUnlock), - }; -}()); +export const TabNotification = mapGameDataToObject( + GameDatabase.tabNotifications, + config => new TabNotificationState(config) +); diff --git a/javascripts/core/ui/tabs.js b/javascripts/core/ui/tabs.js index d46855ec8..eea652edc 100644 --- a/javascripts/core/ui/tabs.js +++ b/javascripts/core/ui/tabs.js @@ -13,7 +13,7 @@ class SubtabState { } get isPermanentlyHidden() { - return this.config.hideAt <= Pelle.endState; + return this.config.hideAt < GameEnd.endState && !GameEnd.creditsClosed; } get hidable() { @@ -22,13 +22,12 @@ class SubtabState { get isHidden() { if (Enslaved.isRunning || Pelle.hasGalaxyGenerator) return false; - // eslint-disable-next-line no-bitwise return ((player.options.hiddenSubtabBits[this._parent.id] & (1 << this.id)) !== 0) && this.hidable; } get isUnlocked() { - return this.config.condition === undefined || this.config.condition() || player.devMode; + return this.config.condition === undefined || this.config.condition(); } get isAvailable() { @@ -53,14 +52,14 @@ class SubtabState { unhideTab() { this._parent.unhideTab(); - // eslint-disable-next-line no-bitwise player.options.hiddenSubtabBits[this._parent.id] &= ~(1 << this.id); } toggleVisibility() { if (this._parent.id === Tabs.current.id && this.id === Tabs.current._currentSubtab.id) return; - // eslint-disable-next-line no-bitwise player.options.hiddenSubtabBits[this._parent.id] ^= (1 << this.id); + + checkTabVisibilityForSecretAchievement(); } get isOpen() { @@ -99,7 +98,7 @@ class TabState { } get isPermanentlyHidden() { - return this.config.hideAt <= Pelle.endState; + return this.config.hideAt < GameEnd.endState && !GameEnd.creditsClosed; } get hidable() { @@ -107,14 +106,13 @@ class TabState { } get isHidden() { - if (Enslaved.isRunning || Pelle.isDoomed) return false; + if (Enslaved.isRunning || Pelle.hasGalaxyGenerator) return false; const hasVisibleSubtab = this.subtabs.some(t => t.isAvailable); - // eslint-disable-next-line no-bitwise return (((player.options.hiddenTabBits & (1 << this.id)) !== 0) || !hasVisibleSubtab) && this.hidable; } get isUnlocked() { - return this.config.condition === undefined || this.config.condition() || player.devMode; + return this.config.condition === undefined || this.config.condition(); } get isAvailable() { @@ -130,7 +128,7 @@ class TabState { } show(manual, subtab = undefined) { - if (!manual && !player.options.automaticTabSwitching) return; + if (!manual && !player.options.automaticTabSwitching || Quote.isOpen) return; ui.view.tab = this.key; if (subtab === undefined) { this._currentSubtab = findLastOpenSubtab(this.id, this.subtabs); @@ -153,14 +151,14 @@ class TabState { } unhideTab() { - // eslint-disable-next-line no-bitwise player.options.hiddenTabBits &= ~(1 << this.id); } toggleVisibility() { if (this.id === Tabs.current.id) return; - // eslint-disable-next-line no-bitwise player.options.hiddenTabBits ^= (1 << this.id); + + checkTabVisibilityForSecretAchievement(); } resetToAvailable() { @@ -219,6 +217,11 @@ export const Tabs = (function() { }; }()); +const checkTabVisibilityForSecretAchievement = () => { + // Checks if every unlocked tab that is hidable is hidden + if (Tabs.all.filter(t => t.isUnlocked && t.hidable).every(t => t.isHidden)) SecretAchievement(47).unlock(); +}; + EventHub.logic.on(GAME_EVENT.TAB_CHANGED, () => { const currTab = Tabs.current.id; player.options.lastOpenTab = currTab; diff --git a/javascripts/core/utils.js b/javascripts/core/utils.js index 4365ea8e3..c0c69d79b 100644 --- a/javascripts/core/utils.js +++ b/javascripts/core/utils.js @@ -1,14 +1,14 @@ // Stuff to be loaded into global before anything else -import "./polyfill.js"; -import "./extensions.js"; +import "./polyfill"; +import "./extensions"; -import "./crash.js"; -import "./timespan.js"; -import "./format.js"; -import "./constants.js"; -import "./math.js"; -import "./async-utils.js"; -import "./event-hub.js"; +import "./crash"; +import "./timespan"; +import "./format"; +import "./constants"; +import "./math"; +import "./async-utils"; +import "./event-hub"; -export * from "./game-mechanics/index.js"; +export * from "./game-mechanics/index"; diff --git a/javascripts/core/wordShift.js b/javascripts/core/wordShift.js new file mode 100644 index 000000000..7ba3e0df7 --- /dev/null +++ b/javascripts/core/wordShift.js @@ -0,0 +1,68 @@ +function predictableRandom(x) { + let start = Math.pow(x % 97, 4.3) * 232344573; + const a = 15485863; + const b = 521791; + start = (start * a) % b; + for (let i = 0; i < (x * x) % 90 + 90; i++) { + start = (start * a) % b; + } + return start / b; +} + +function randomSymbol() { + return String.fromCharCode(Math.floor(Math.random() * 50) + 192); +} + +export default { + // Word cycling uses two diffrent effects to smoothly ease between words in the randomized set + // - The randomization effect eases in and out smoothly, with about 62% in the time in the middle being + // completely unrandomized (randomCrossWords is passed frac <= 0). The randomization parameter goes well above 1 + // in order to have a good chance of properly randomizing the entire input in the middle + // - Near the "edges" (12% on each side) of each word's randomization time, it's blended with the previous or next + // word. This mostly serves to smoothly ease between strings of different lengths, and only occurs between + // strings which already have a high randomization fraction (frac > 1.3) + wordCycle(list, noBuffer = false) { + const len = list.length; + const tick = Math.floor(Date.now() / 250) % (len * 5); + const mod5 = ((Date.now() / 250) % (len * 5)) % 5; + const largeTick = Math.floor(tick / 5); + let v = list[largeTick]; + + // Blend with adjacent words, in such a way that mod5 being 0 or 5 corresponds with a 0.5 blend parameter + if (mod5 < 0.6) { + v = this.blendWords(list[(largeTick + list.length - 1) % list.length], list[largeTick], (mod5 + 0.6) / 1.2); + } else if (mod5 > 4.4) { + v = this.blendWords(list[largeTick], list[(largeTick + 1) % list.length], (mod5 - 4.4) / 1.2); + } + + v = this.randomCrossWords(v, 0.1 * Math.pow(mod5 - 2.5, 4) - 0.6); + if (noBuffer) return v; + + const maxWordLen = Math.max(...list.map(x => x.length)); + const bufferSpace = (maxWordLen - v.length) / 2; + + // Buffer the result with ALT+255 on either side to prevent the ui from twitching. + // Spaces do not work due to being automatically collapsed, and css fixing this causes other issues. + return " ".repeat(Math.ceil(bufferSpace)) + v + " ".repeat(Math.floor(bufferSpace)); + }, + // Note that while frac may appear to specify the proportion of letters randomized, it may end up being slightly less + // depending on the specific string length and random output sometimes giving outputs which aren't coprime + randomCrossWords(str, frac = 0.7) { + if (frac <= 0) return str; + const x = str.split(""); + for (let i = 0; i < x.length * frac; i++) { + const randomIndex = Math.floor(predictableRandom(Math.floor(Date.now() / 500) % 964372 + 1.618 * i) * x.length); + x[randomIndex] = randomSymbol(); + } + return x.join(""); + }, + // This should only be used on words which will end up being completely randomized, because the unscrambled appearance + // of the output may look bad. Blends two strings together to produce a string of intermediate length, taking a + // specifed fraction (param, 0 to 1) from the first word and the rest (1 - param) from the second + blendWords(first, second, param) { + if (param <= 0) return first; + if (param >= 1) return second; + return first.substring(0, first.length * (1 - param)) + + second.substring(second.length * (1 - param), second.length); + } +}; diff --git a/javascripts/game.js b/javascripts/game.js index 96e403d20..3b71820f9 100644 --- a/javascripts/game.js +++ b/javascripts/game.js @@ -1,9 +1,11 @@ -import { playFabLogin } from "./core/playfab.js"; -import { DC } from "./core/constants.js"; -import { SpeedrunMilestones } from "./core/speedrun.js"; import TWEEN from "tween.js"; + +import { DC } from "./core/constants"; import { deepmergeAll } from "@/utility/deepmerge"; +import { playFabLogin } from "./core/playfab"; +import { SpeedrunMilestones } from "./core/speedrun"; import { supportedBrowsers } from "./supported-browsers"; +import Payments from "./core/payments"; if (GlobalErrorHandler.handled) { throw new Error("Initialization failed"); @@ -84,14 +86,10 @@ export function gainedInfinityPoints() { Achievement(103), TimeStudy(111) ); - const mult = NG.multiplier; - const pow = NG.power; if (Pelle.isDisabled("IPMults")) { return Decimal.pow10(player.records.thisInfinity.maxAM.log10() / div - 0.75) - .timesEffectsOf(PelleRifts.famine) + .timesEffectsOf(PelleRifts.vacuum) .times(Pelle.specialGlyphEffect.infinity) - .times(mult) - .pow(pow) .floor(); } let ip = player.break @@ -100,7 +98,6 @@ export function gainedInfinityPoints() { if (Effarig.isRunning && Effarig.currentStage === EFFARIG_STAGES.ETERNITY) { ip = ip.min(DC.E200); } - ip = ip.times(mult); ip = ip.times(GameCache.totalIPMult.value); if (Teresa.isRunning) { ip = ip.pow(0.55); @@ -113,15 +110,13 @@ export function gainedInfinityPoints() { ip = ip.pow(getSecondaryGlyphEffect("infinityIP")); } - ip = ip.pow(pow); return ip.floor(); } function totalEPMult() { - const totalMult = new Decimal(NG.multiplier); return Pelle.isDisabled("EPMults") - ? totalMult.times(Pelle.specialGlyphEffect.time.timesEffectOf(PelleRifts.famine.milestones[2])) - : totalMult.times(getAdjustedGlyphEffect("cursedEP")) + ? Pelle.specialGlyphEffect.time.timesEffectOf(PelleRifts.vacuum.milestones[2]) + : getAdjustedGlyphEffect("cursedEP") .times(ShopPurchase.EPPurchases.currentMult) .timesEffectsOf( EternityUpgrade.epMult, @@ -135,9 +130,8 @@ function totalEPMult() { } export function gainedEternityPoints() { - const pow = NG.power; let ep = DC.D5.pow(player.records.thisEternity.maxIP.plus( - gainedInfinityPoints()).log10() / (308 - PelleRifts.war.effectValue.toNumber()) - 0.7).times(totalEPMult()); + gainedInfinityPoints()).log10() / (308 - PelleRifts.recursion.effectValue.toNumber()) - 0.7).times(totalEPMult()); if (Teresa.isRunning) { ep = ep.pow(0.55); @@ -150,11 +144,11 @@ export function gainedEternityPoints() { ep = ep.pow(getSecondaryGlyphEffect("timeEP")); } - return ep.pow(pow).floor(); + return ep.floor(); } export function requiredIPForEP(epAmount) { - return Decimal.pow10(308 * (Decimal.log(Decimal.divide(Math.pow(epAmount, 1 / NG.power), totalEPMult()), 5) + 0.7)) + return Decimal.pow10(308 * (Decimal.log(Decimal.divide(epAmount, totalEPMult()), 5) + 0.7)) .clampMin(Number.MAX_VALUE); } @@ -282,10 +276,10 @@ export function gainedInfinities() { TimeStudy(32), RealityUpgrade(5), RealityUpgrade(7), - Achievement(164) + Achievement(164), + Ra.unlocks.continuousTTBoost.effects.infinity ); infGain = infGain.times(getAdjustedGlyphEffect("infinityinfmult")); - infGain = infGain.times(RA_UNLOCKS.TT_BOOST.effect.infinity()); infGain = infGain.powEffectOf(SingularityMilestone.infinitiedPow); return infGain; } @@ -337,17 +331,13 @@ export function getGameSpeedupFactor(effectsToConsider, blackHolesActiveOverride : blackHole.id <= blackHolesActiveOverride; if (!isActive) break; factor *= Math.pow(blackHole.power, BlackHoles.unpauseAccelerationFactor); - if (V.has(V_UNLOCKS.ACHIEVEMENT_BH)) { - factor *= V_UNLOCKS.ACHIEVEMENT_BH.effect(); - } + factor *= VUnlocks.achievementBH.effectOrDefault(1); } } } if (effects.includes(GAME_SPEED_EFFECT.SINGULARITY_MILESTONE)) { - factor *= SingularityMilestone.gamespeedFromSingularities.canBeApplied - ? SingularityMilestone.gamespeedFromSingularities.effectValue - : 1; + factor *= SingularityMilestone.gamespeedFromSingularities.effectOrDefault(1); } if (effects.includes(GAME_SPEED_EFFECT.TIME_GLYPH)) { @@ -405,15 +395,22 @@ export function getGameSpeedupForDisplay() { // TODO: Clean this up, remove the disable line // eslint-disable-next-line complexity export function gameLoop(passDiff, options = {}) { - let diff = passDiff; PerformanceStats.start("Frame Time"); PerformanceStats.start("Game Update"); + EventHub.dispatch(GAME_EVENT.GAME_TICK_BEFORE); + + let diff = passDiff; const thisUpdate = Date.now(); const realDiff = diff === undefined ? Math.clamp(thisUpdate - player.lastUpdate, 1, 21600000) : diff; + if (GameEnd.creditsEverClosed) { + GameUI.update(); + return; + } + // We want to allow for a speedrunner to be able to adjust their visual settings before actually starting the run, // which means that we need to effectively halt the game loop until the official start if (Speedrun.isPausedAtStart()) { @@ -442,7 +439,7 @@ export function gameLoop(passDiff, options = {}) { return; } - // Ra-Enslaved auto-release stored time (once every 5 ticks) + // Ra-Nameless auto-release stored time (once every 5 ticks) if (Enslaved.isAutoReleasing) { Enslaved.autoReleaseTick++; } @@ -489,9 +486,7 @@ export function gameLoop(passDiff, options = {}) { const reducedTimeFactor = getGameSpeedupFactor(); const totalTimeFactor = getGameSpeedupFactor([GAME_SPEED_EFFECT.FIXED_SPEED, GAME_SPEED_EFFECT.TIME_GLYPH, GAME_SPEED_EFFECT.BLACK_HOLE, GAME_SPEED_EFFECT.SINGULARITY_MILESTONE]); - const amplification = Ra.has(RA_UNLOCKS.IMPROVED_STORED_TIME) - ? RA_UNLOCKS.IMPROVED_STORED_TIME.effect.gameTimeAmplification() - : 1; + const amplification = Ra.unlocks.improvedStoredTime.effects.gameTimeAmplification.effectOrDefault(1); const beforeStore = player.celestials.enslaved.stored; player.celestials.enslaved.stored = Math.clampMax(player.celestials.enslaved.stored + diff * (totalTimeFactor - reducedTimeFactor) * amplification, Enslaved.timeCap); @@ -543,7 +538,7 @@ export function gameLoop(passDiff, options = {}) { Currency.realities.add(uncountabilityGain); Currency.perkPoints.add(uncountabilityGain); - if (Perk.autocompleteEC1.isBought && player.reality.autoEC) player.reality.lastAutoEC += realDiff; + if (Perk.autocompleteEC1.canBeApplied && player.reality.autoEC) player.reality.lastAutoEC += realDiff; EternityChallenge(12).tryFail(); Achievements._power.invalidate(); @@ -583,7 +578,7 @@ export function gameLoop(passDiff, options = {}) { // Unlocks dilation at a certain total TT count for free, but we add the cost first in order to make // sure that TT count doesn't go negative and that we can actually buy it. This technically bumps the max theorem // amount up as well, but at this point of the game 5k TT is insignificant to basically all other sources of TT. - if (Ra.has(RA_UNLOCKS.AUTO_DILATION_UNLOCK) && + if (Ra.unlocks.autoUnlockDilation.canBeApplied && Currency.timeTheorems.max.gte(TimeStudy.dilation.totalTimeTheoremRequirement) && !isInCelestialReality() && !Pelle.isDoomed) { @@ -594,13 +589,17 @@ export function gameLoop(passDiff, options = {}) { applyAutoUnlockPerks(); if (GlyphSelection.active) GlyphSelection.update(gainedGlyphLevel()); - if (player.dilation.active && Ra.has(RA_UNLOCKS.AUTO_TP) && !Pelle.isDoomed) rewardTP(); + // There are some external checks which prevent excessive resource gain with Teresa-25; it may give TP outside of + // dilation, but the TP gain function is also coded to behave differently if it's active + const teresa1 = player.dilation.active && Ra.unlocks.autoTP.canBeApplied; + const teresa25 = !isInCelestialReality() && Ra.unlocks.unlockDilationStartingTP.canBeApplied; + if ((teresa1 || teresa25) && !Pelle.isDoomed) rewardTP(); if (!EnslavedProgress.hintsUnlocked.hasProgress && Enslaved.has(ENSLAVED_UNLOCKS.RUN) && !Enslaved.isCompleted) { player.celestials.enslaved.hintUnlockProgress += Enslaved.isRunning ? realDiff : realDiff / 25; if (player.celestials.enslaved.hintUnlockProgress >= TimeSpan.fromHours(5).totalMilliseconds) { EnslavedProgress.hintsUnlocked.giveProgress(); - Enslaved.quotes.show(Enslaved.quotes.HINT_UNLOCK); + Enslaved.quotes.hintUnlock.show(); } } @@ -610,6 +609,11 @@ export function gameLoop(passDiff, options = {}) { AutomatorBackend.update(realDiff); Pelle.gameLoop(realDiff); GalaxyGenerator.loop(realDiff); + GameEnd.gameLoop(realDiff); + + if (!Enslaved.canAmplify) { + Enslaved.boostReality = false; + } if (Tabs.current.isPermanentlyHidden) { const tab = Tabs.all.reverse().find(t => !t.isPermanentlyHidden && t.id !== 10); @@ -649,10 +653,10 @@ function passivePrestigeGen() { infGen = infGen.plus(0.2 * Time.deltaTimeMs / Math.clampMin(33, player.records.bestInfinity.time)); infGen = infGen.timesEffectsOf( RealityUpgrade(5), - RealityUpgrade(7) + RealityUpgrade(7), + Ra.unlocks.continuousTTBoost.effects.infinity ); infGen = infGen.times(getAdjustedGlyphEffect("infinityinfmult")); - infGen = infGen.times(RA_UNLOCKS.TT_BOOST.effect.infinity()); } if (RealityUpgrade(11).isBought) { infGen = infGen.plus(RealityUpgrade(11).effectValue.times(Time.deltaTime)); @@ -674,14 +678,13 @@ function passivePrestigeGen() { // Applies all perks which automatically unlock things when passing certain thresholds, needs to be checked every tick function applyAutoUnlockPerks() { - if (Pelle.isDoomed) return; - if (!TimeDimension(8).isUnlocked && Perk.autounlockTD.isBought) { + if (!TimeDimension(8).isUnlocked && Perk.autounlockTD.canBeApplied) { for (let dim = 5; dim <= 8; ++dim) TimeStudy.timeDimension(dim).purchase(); } - if (Perk.autounlockDilation3.isBought) buyDilationUpgrade(DilationUpgrade.ttGenerator.id); - if (Perk.autounlockReality.isBought) TimeStudy.reality.purchase(true); - if (player.eternityUpgrades.size < 6 && Perk.autounlockEU2.isBought) { - const secondRow = Object.values(EternityUpgrade).filter(u => u.id > 3); + if (Perk.autounlockDilation3.canBeApplied) buyDilationUpgrade(DilationUpgrade.ttGenerator.id); + if (Perk.autounlockReality.canBeApplied) TimeStudy.reality.purchase(true); + if (player.eternityUpgrades.size < 6 && Perk.autounlockEU2.canBeApplied) { + const secondRow = EternityUpgrade.all.filter(u => u.id > 3); for (const upgrade of secondRow) { if (player.eternityPoints.gte(upgrade.cost / 1e10)) player.eternityUpgrades.add(upgrade.id); } @@ -711,9 +714,9 @@ function laitelaRealityTick(realDiff) { laitelaInfo.difficultyTier++; laitelaInfo.fastestCompletion = 300; completionText += laitelaBeatText(Laitela.maxAllowedDimension + 1); - for (const quote of Object.values(Laitela.quotes)) { - if (laitelaInfo.difficultyTier >= quote.destabilize) { - Laitela.quotes.show(quote); + for (const quote of Laitela.quotes.all) { + if (quote.requirement) { + quote.show(); } } } @@ -730,7 +733,7 @@ function laitelaRealityTick(realDiff) { ${TimeSpan.fromSeconds(laitelaInfo.fastestCompletion).toStringShort()} to improve your multiplier.`; } if (Laitela.isFullyDestabilized) SpeedrunMilestones(24).tryComplete(); - Modal.message.show(completionText); + Modal.message.show(completionText, {}, 2); } } @@ -739,9 +742,10 @@ function laitelaBeatText(disabledDim) { case 1: return `

Lai'tela's Reality will now completely disable production from all Dimensions. The Reality can still be entered, but further destabilization is no longer possible. For completely destabilizing the Reality, you also get an additional ${formatX(8)} to Dark Energy gain.`; - case 2: - case 3: return `

Lai'tela's Reality will now disable production from all - ${disabledDim}${disabledDim === 2 ? "nd" : "rd"} Dimensions during + case 2: return `

Lai'tela's Reality will now disable production from all 2nd Dimensions during + future runs, but the reward will be ${formatInt(100)} times stronger than before. Completely destabilizing + the Reality for the final Dimension will give you an additional ${formatX(8)} to Dark Energy gain.`; + case 3: return `

Lai'tela's Reality will now disable production from all 3rd Dimensions during future runs, but the reward will be ${formatInt(100)} times stronger than before.`; case 8: return `

Lai'tela's Reality will now disable production from all 8th Dimensions during future runs, but the reward will be ${formatInt(100)} times stronger than before. This boost can be @@ -756,9 +760,9 @@ function laitelaBeatText(disabledDim) { function applyAutoprestige(diff) { Currency.infinityPoints.add(TimeStudy(181).effectOrDefault(0)); - if (Teresa.has(TERESA_UNLOCKS.EPGEN) && !Pelle.isDisabled("EPgen")) { + if (TeresaUnlocks.epGen.canBeApplied) { Currency.eternityPoints.add(player.records.thisEternity.bestEPmin.times(DC.D0_01) - .times(getGameSpeedupFactor() * diff / 1000).times(RA_UNLOCKS.TT_BOOST.effect.autoPrestige())); + .times(getGameSpeedupFactor() * diff / 1000).timesEffectOf(Ra.unlocks.continuousTTBoost.effects.autoPrestige)); } if (InfinityUpgrade.ipGen.isCharged) { @@ -795,9 +799,11 @@ function updateTachyonGalaxies() { export function getTTPerSecond() { // All TT multipliers (note that this is equal to 1 pre-Ra) - let ttMult = RA_UNLOCKS.TT_BOOST.effect.ttGen(); - ttMult *= Achievement(137).effectOrDefault(1); - if (Ra.has(RA_UNLOCKS.TT_ACHIEVEMENT)) ttMult *= RA_UNLOCKS.TT_ACHIEVEMENT.effect(); + let ttMult = Effects.product( + Ra.unlocks.continuousTTBoost.effects.ttGen, + Ra.unlocks.achievementTTMult, + Achievement(137), + ); if (GlyphAlteration.isAdded("dilation")) ttMult *= getSecondaryGlyphEffect("dilationTTgen"); // Glyph TT generation @@ -812,8 +818,8 @@ export function getTTPerSecond() { // Lai'tela TT power let finalTT = dilationTT.add(glyphTT); - if (SingularityMilestone.theoremPowerFromSingularities.isUnlocked && finalTT.gt(1) && !Pelle.isDoomed) { - finalTT = finalTT.pow(SingularityMilestone.theoremPowerFromSingularities.effectValue); + if (finalTT.gt(1)) { + finalTT = finalTT.pow(SingularityMilestone.theoremPowerFromSingularities.effectOrDefault(1)); } return finalTT; @@ -834,12 +840,6 @@ function afterSimulation(seconds, playerBefore) { GameUI.notify.showBlackHoles = true; } -const OFFLINE_BH_PAUSE_STATE = { - ACTIVE: 0, - INACTIVE: 1, - PAUSED: 2, -}; - export function simulateTime(seconds, real, fast) { // The game is simulated at a base 50ms update rate, with a max of // player.options.offlineTicks ticks. additional ticks are converted @@ -881,6 +881,8 @@ export function simulateTime(seconds, real, fast) { Currency.infinityPoints.add(player.records.thisEternity.bestIPMsWithoutMaxAll.times(seconds * 1000 / 2)); } + EventHub.dispatch(GAME_EVENT.OFFLINE_CURRENCY_GAINED); + let remainingRealSeconds = seconds; // During async code the number of ticks remaining can go down suddenly // from "Speed up" which means tick length needs to go up, and thus @@ -899,76 +901,17 @@ export function simulateTime(seconds, real, fast) { }; // Simulation code which accounts for BH cycles (segments where a BH is active doesn't use diff since it splits - // up intervals based on real time instead in an effort to keep ticks all roughly equal in game time). With black - // hole auto-pausing, the simulation now becomes a three-step process: - // 1. Simulate until the BH dectivates (this only occurs if it's active when the simulation starts) - // 2. At this point, the BH we're tracking is inactive and timeToNextStateChange will return the proper value until - // we should pause it, so we run until we either hit that or run out of time (this often takes very few ticks) - // 3. The BH is now paused and the simpler code works to finish the rest of the ticks - let offlineBHState = OFFLINE_BH_PAUSE_STATE.ACTIVE; - const trackedBH = player.blackHoleAutoPauseMode; + // up intervals based on real time instead in an effort to keep ticks all roughly equal in game time). + // Black hole auto-pausing is entirely handled by the black hole phase advancement code (for actually pausing) + // and calculateOfflineTick (for time calculation). if (BlackHoles.areUnlocked && !BlackHoles.arePaused) { - if (trackedBH === 0) { - // Auto-pause is off, don't bother doing anything fancy - loopFn = i => { - const [realTickTime, blackHoleSpeedup] = BlackHoles.calculateOfflineTick(remainingRealSeconds, - i, 0.0001); - remainingRealSeconds -= realTickTime; - gameLoop(1000 * realTickTime, { blackHoleSpeedup }); - }; - } else { - if (!BlackHole(trackedBH).isActive) offlineBHState++; - loopFn = i => { - let realTickTime, blackHoleSpeedup, limit, diff; - switch (offlineBHState) { - case OFFLINE_BH_PAUSE_STATE.ACTIVE: - // If we have to reduce tick length to not overshoot the transition, we also advance the simulation state - // We skip past the BH going inactive by 1 ms in order to ensure that the next simulation step actually has - // an inactive BH in order for the logic to work out - [realTickTime, blackHoleSpeedup] = BlackHoles.calculateOfflineTick(remainingRealSeconds, - i, 0.0001); - limit = BlackHole(trackedBH).timeToNextStateChange + 0.001; - if (realTickTime > limit) { - remainingRealSeconds -= limit; - gameLoop(1000 * limit, { blackHoleSpeedup }); - offlineBHState++; - } else { - remainingRealSeconds -= realTickTime; - gameLoop(1000 * realTickTime, { blackHoleSpeedup }); - } - break; - case OFFLINE_BH_PAUSE_STATE.INACTIVE: - // Same as above, but this time the extra 1 ms serves the purpose of putting the game past the auto-pause - // threshold. Otherwise, it'll immediately auto-pause once more when online - [realTickTime, blackHoleSpeedup] = BlackHoles.calculateOfflineTick(remainingRealSeconds, - i, 0.0001); - limit = BlackHole(trackedBH).timeToNextStateChange - BlackHoles.ACCELERATION_TIME + 0.001; - if (realTickTime > limit) { - remainingRealSeconds -= limit; - gameLoop(1000 * limit, { blackHoleSpeedup }); - offlineBHState++; - } else { - remainingRealSeconds -= realTickTime; - gameLoop(1000 * realTickTime, { blackHoleSpeedup }); - } - break; - case OFFLINE_BH_PAUSE_STATE.PAUSED: - // At this point the BH is paused and we just use the same code as no BH at all. This isn't necessarily - // executed in all situations; for example short offline periods may not reach this code - diff = remainingRealSeconds / i; - gameLoop(1000 * diff); - remainingRealSeconds -= diff; - break; - } - }; - } + loopFn = i => { + const [realTickTime, blackHoleSpeedup] = BlackHoles.calculateOfflineTick(remainingRealSeconds, + i, 0.0001); + remainingRealSeconds -= realTickTime; + gameLoop(1000 * realTickTime, { blackHoleSpeedup }); + }; } - const oldLoopFn = loopFn; - loopFn = i => { - Pelle.addAdditionalEnd = false; - oldLoopFn(i); - Pelle.addAdditionalEnd = true; - }; // We don't show the offline modal here or bother with async if doing a fast simulation if (fast) { @@ -1060,7 +1003,6 @@ window.onload = function() { } } document.getElementById("loading").style.display = "none"; - document.body.style.overflowY = "auto"; }, 500); if (!supportedBrowser) { GameIntervals.stop(); diff --git a/jsconfig.json b/jsconfig.json index 24220941e..acfd26d5a 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,7 +1,11 @@ { - "compilerOptions": { - "module": "ES6", - "target": "es6" - }, - "include": ["src/**/*", "javascripts/**/*"] - } \ No newline at end of file + "compilerOptions": { + "module": "ES6", + "target": "es6" + }, + "include": [ + "src/**/*", + "javascripts/**/*" + ], + "noEmit": true +} diff --git a/package-lock.json b/package-lock.json index 9d965c854..4d685e193 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "dependencies": { - "@antimatter-dimensions/notations": "^2.2.0", + "@antimatter-dimensions/notations": "^3.0.3", "@fortawesome/fontawesome-free": "^6.1.1", "break_infinity.js": "^1.3.0", "chevrotain": "^4.8.1", @@ -26,6 +26,7 @@ "vis-util": "^4.0.0", "vue": "^2.6.11", "vue-gtag": "^1.16.1", + "vue-loading-overlay": "^3.4.2", "vue-splitpane": "^1.0.6", "vuedraggable": "^2.24.3" }, @@ -37,15 +38,22 @@ "@vue/cli-service": "^5.0.0-rc.0", "browserslist-useragent-regexp": "^3.0.2", "eslint": "^7.20.0", + "eslint-import-resolver-alias": "^1.1.2", + "eslint-plugin-import": "^2.26.0", "eslint-plugin-vue": "^8.0.3", + "postcss-html": "^1.4.1", + "stylelint": "^14.8.2", + "stylelint-config-recommended-vue": "^1.4.0", + "stylelint-config-standard": "^25.0.0", + "stylelint-order": "^5.0.0", "vue-template-compiler": "^2.6.11", "webpack": "^5.64.0" } }, "node_modules/@antimatter-dimensions/notations": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@antimatter-dimensions/notations/-/notations-2.2.0.tgz", - "integrity": "sha512-EGXWMQer0Bvb1kujon6rvZdj+3k533SlfLtgoz+y35hMhsvQNoduURpZ0j+apFSpC0vpeEvVUjXqq6eUmQrJZQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@antimatter-dimensions/notations/-/notations-3.0.3.tgz", + "integrity": "sha512-ldCH7P0G8saC1nE8D04hRZ3tUVjQ2YHkIBKowAfyTzZEppZtxpR6Zl+GqBE9lhT1Jv+no0xIKWm3QttQ5qODLg==", "dependencies": { "break_infinity.js": "^1.2.0", "tslib": "^2.0.0" @@ -2908,6 +2916,12 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, "node_modules/@types/long": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", @@ -4934,15 +4948,6 @@ } } }, - "node_modules/@vue/cli-service/node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/@vue/cli-service/node_modules/@vue/cli-shared-utils": { "version": "5.0.0-rc.0", "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.0-rc.0.tgz", @@ -5119,27 +5124,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@vue/cli-service/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@vue/cli-service/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@vue/cli-service/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5203,18 +5187,6 @@ } } }, - "node_modules/@vue/cli-service/node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@vue/cli-service/node_modules/enhanced-resolve": { "version": "5.8.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", @@ -5241,60 +5213,12 @@ "node": ">=8.0.0" } }, - "node_modules/@vue/cli-service/node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@vue/cli-service/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@vue/cli-service/node_modules/glob-to-regexp": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, - "node_modules/@vue/cli-service/node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@vue/cli-service/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -5304,24 +5228,6 @@ "node": ">=8" } }, - "node_modules/@vue/cli-service/node_modules/ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/@vue/cli-service/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/@vue/cli-service/node_modules/loader-runner": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", @@ -5373,19 +5279,6 @@ "node": ">=10" } }, - "node_modules/@vue/cli-service/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/@vue/cli-service/node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -5456,15 +5349,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@vue/cli-service/node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/@vue/cli-service/node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -5520,15 +5404,6 @@ "randombytes": "^2.1.0" } }, - "node_modules/@vue/cli-service/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/@vue/cli-service/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -5654,18 +5529,6 @@ "webpack": "^4.27.0 || ^5.0.0" } }, - "node_modules/@vue/cli-service/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/@vue/cli-service/node_modules/watchpack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz", @@ -6320,6 +6183,25 @@ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", "dev": true }, + "node_modules/array-includes": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -6329,6 +6211,33 @@ "node": ">=8" } }, + "node_modules/array.prototype.flat": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -6631,6 +6540,18 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/break_infinity.js": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/break_infinity.js/-/break_infinity.js-1.3.0.tgz", @@ -6840,6 +6761,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -6962,51 +6909,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/chokidar/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/chokidar/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -7214,6 +7116,18 @@ "node": ">=6" } }, + "node_modules/clone-regexp": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", + "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", + "dev": true, + "dependencies": { + "is-regexp": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/codemirror": { "version": "5.65.1", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.1.tgz", @@ -7235,9 +7149,9 @@ "dev": true }, "node_modules/colord": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.1.tgz", - "integrity": "sha512-4LBMSt09vR0uLnPVkOUBnmxgoaeN4ewRbx801wY/bXcltXfpR/G46OdWn96XpYmCWuYvO46aBZP4NgX8HpNAcw==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", + "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", "dev": true }, "node_modules/colorette": { @@ -7388,88 +7302,6 @@ "webpack": "^5.1.0" } }, - "node_modules/copy-webpack-plugin/node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/copy-webpack-plugin/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/copy-webpack-plugin/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/copy-webpack-plugin/node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/copy-webpack-plugin/node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/copy-webpack-plugin/node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/copy-webpack-plugin/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/copy-webpack-plugin/node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -7482,66 +7314,6 @@ "node": ">=10.13.0" } }, - "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/copy-webpack-plugin/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/copy-webpack-plugin/node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/copy-webpack-plugin/node_modules/schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", @@ -7569,27 +7341,6 @@ "randombytes": "^2.1.0" } }, - "node_modules/copy-webpack-plugin/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/copy-webpack-plugin/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/core-js": { "version": "3.19.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.19.1.tgz", @@ -7693,6 +7444,15 @@ "postcss": "^8.0.9" } }, + "node_modules/css-functions-list": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.0.1.tgz", + "integrity": "sha512-PriDuifDt4u4rkDgnqRCLnjfMatufLmWNfQnGCq34xZwpY3oabwhB9SqRBmuvWUgndbemCFlKqg+nO7C2q0SBw==", + "dev": true, + "engines": { + "node": ">=12.22" + } + }, "node_modules/css-loader": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", @@ -7989,6 +7749,37 @@ "ms": "2.0.0" } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/deep-equal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", @@ -8194,15 +7985,19 @@ } }, "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "dev": true, "dependencies": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/del": { @@ -8227,136 +8022,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/del/node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/del/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/del/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/del/node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/del/node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/del/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/del/node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/del/node_modules/ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/del/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/del/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/del/node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/del/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -8381,18 +8046,6 @@ "node": ">=8" } }, - "node_modules/del/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -8686,12 +8339,75 @@ "stackframe": "^1.1.1" } }, + "node_modules/es-abstract": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.0.tgz", + "integrity": "sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.1", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-module-lexer": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", "dev": true }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -8772,6 +8488,177 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-import-resolver-alias": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz", + "integrity": "sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==", + "dev": true, + "engines": { + "node": ">= 4" + }, + "peerDependencies": { + "eslint-plugin-import": ">=1.4.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/eslint-module-utils/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", + "has": "^1.0.3", + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eslint-plugin-vue": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.0.3.tgz", @@ -8911,52 +8798,6 @@ "webpack": "^5.0.0" } }, - "node_modules/eslint-webpack-plugin/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", @@ -8975,18 +8816,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/eslint-webpack-plugin/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/eslint/node_modules/@babel/code-frame": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", @@ -9392,6 +9221,18 @@ "node": ">=6" } }, + "node_modules/execall": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", + "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", + "dev": true, + "dependencies": { + "clone-regexp": "^2.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -9455,9 +9296,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -9467,65 +9308,7 @@ "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" - } - }, - "node_modules/fast-glob/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fast-glob/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fast-glob/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/fast-glob/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/fast-glob/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" + "node": ">=8.6.0" } }, "node_modules/fast-json-stable-stringify": { @@ -9540,6 +9323,12 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "node_modules/fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -9572,6 +9361,18 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -9784,12 +9585,39 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gamma": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gamma/-/gamma-1.0.0.tgz", @@ -9826,6 +9654,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -9838,6 +9678,22 @@ "node": ">=6" } }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -9875,6 +9731,32 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -9885,16 +9767,16 @@ } }, "node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -9905,14 +9787,20 @@ } }, "node_modules/globby/node_modules/ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true, "engines": { "node": ">= 4" } }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", + "dev": true + }, "node_modules/graceful-fs": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", @@ -9940,6 +9828,15 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -9952,6 +9849,15 @@ "node": ">= 0.4.0" } }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -9961,10 +9867,22 @@ "node": ">=4" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, "engines": { "node": ">= 0.4" @@ -10099,12 +10017,15 @@ "dev": true }, "node_modules/html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/html-webpack-plugin": { @@ -10221,64 +10142,6 @@ "node": ">=12.0.0" } }, - "node_modules/http-proxy-middleware/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/http-proxy-middleware/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/http-proxy-middleware/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/http-proxy-middleware/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -10367,6 +10230,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -10399,6 +10271,12 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, "node_modules/internal-ip": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-6.2.0.tgz", @@ -10426,6 +10304,20 @@ "node": ">= 0.10" } }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -10481,6 +10373,18 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -10493,6 +10397,34 @@ "node": ">=8" } }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-ci": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", @@ -10506,9 +10438,9 @@ } }, "node_modules/is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -10606,6 +10538,42 @@ "node": ">=8" } }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -10664,12 +10632,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-regexp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", + "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -10679,6 +10668,36 @@ "node": ">=0.10.0" } }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -10691,6 +10710,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -10942,6 +10973,12 @@ "node": ">= 8" } }, + "node_modules/known-css-properties": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz", + "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==", + "dev": true + }, "node_modules/launch-editor": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.2.1.tgz", @@ -11329,6 +11366,28 @@ "semver": "bin/semver.js" } }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -11356,6 +11415,104 @@ "node": ">= 4.0.0" } }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/meow/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/meow/node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/meow/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -11404,6 +11561,19 @@ "node": ">= 0.6" } }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -11446,6 +11616,15 @@ "node": ">=4" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/mini-css-extract-plugin": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.4.tgz", @@ -11490,9 +11669,9 @@ "dev": true }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -11501,11 +11680,34 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minimist-options/node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/minipass": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", @@ -11592,9 +11794,9 @@ } }, "node_modules/nanoid": { - "version": "3.1.30", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", - "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -11717,6 +11919,12 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-selector": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/normalize-selector/-/normalize-selector-0.2.0.tgz", + "integrity": "sha1-0LFF62kRicY6eNIB3E/bEpPvDAM=", + "dev": true + }, "node_modules/normalize-url": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", @@ -11762,6 +11970,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-is": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", @@ -11805,6 +12022,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -12311,9 +12545,9 @@ "dev": true }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -12374,21 +12608,27 @@ "dev": true }, "node_modules/postcss": { - "version": "8.3.11", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", - "integrity": "sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==", + "version": "8.4.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.13.tgz", + "integrity": "sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], "dependencies": { - "nanoid": "^3.1.30", + "nanoid": "^3.3.3", "picocolors": "^1.0.0", - "source-map-js": "^0.6.2" + "source-map-js": "^1.0.2" }, "engines": { "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" } }, "node_modules/postcss-calc": { @@ -12485,6 +12725,51 @@ "postcss": "^8.2.15" } }, + "node_modules/postcss-html": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.4.1.tgz", + "integrity": "sha512-OKihuWxPuBQrQeLNsavP7ytJ9IYNj/ViAXB2v7Qjh56LnfESKrkahKA9si4VfPN8xtz6oqUE6KdL0bTPrHJr6g==", + "dev": true, + "dependencies": { + "htmlparser2": "^7.1.2", + "postcss": "^8.4.0", + "postcss-safe-parser": "^6.0.0" + }, + "engines": { + "node": "^12 || >=14" + } + }, + "node_modules/postcss-html/node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/postcss-html/node_modules/htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + }, "node_modules/postcss-loader": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.0.tgz", @@ -12540,6 +12825,12 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", + "dev": true + }, "node_modules/postcss-merge-longhand": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.3.tgz", @@ -12888,10 +13179,32 @@ "postcss": "^8.2.15" } }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", + "dev": true + }, + "node_modules/postcss-safe-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "dev": true, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, "node_modules/postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -12901,6 +13214,15 @@ "node": ">=4" } }, + "node_modules/postcss-sorting": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-7.0.1.tgz", + "integrity": "sha512-iLBFYz6VRYyLJEJsBJ8M3TCqNcckVzz4wFounSc5Oez35ogE/X+aoC5fFu103Ot7NyvjU3/xqIXn93Gp3kJk4g==", + "dev": true, + "peerDependencies": { + "postcss": "^8.3.9" + } + }, "node_modules/postcss-svgo": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.3.tgz", @@ -12935,9 +13257,9 @@ } }, "node_modules/postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, "node_modules/prelude-ls": { @@ -13122,6 +13444,15 @@ } ] }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -13231,6 +13562,31 @@ "node": ">=8.10.0" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/redent/node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -13269,13 +13625,14 @@ "integrity": "sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw==" }, "node_modules/regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -13386,13 +13743,17 @@ "dev": true }, "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "dev": true, "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -13707,10 +14068,24 @@ "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", "dev": true }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "node_modules/sirv": { @@ -13822,9 +14197,9 @@ } }, "node_modules/source-map-js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "dev": true, "engines": { "node": ">=0.10.0" @@ -13971,6 +14346,15 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/specificity": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", + "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", + "dev": true, + "bin": { + "specificity": "bin/specificity" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -14019,6 +14403,34 @@ "node": ">=8" } }, + "node_modules/string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -14030,6 +14442,15 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -14069,6 +14490,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=", + "dev": true + }, "node_modules/stylehacks": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.1.tgz", @@ -14085,6 +14512,222 @@ "postcss": "^8.2.15" } }, + "node_modules/stylelint": { + "version": "14.8.2", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.8.2.tgz", + "integrity": "sha512-tjDfexCYfoPdl/xcDJ9Fv+Ko9cvzbDnmdiaqEn3ovXHXasi/hbkt5tSjsiReQ+ENqnz0eltaX/AOO+AlzVdcNA==", + "dev": true, + "dependencies": { + "balanced-match": "^2.0.0", + "colord": "^2.9.2", + "cosmiconfig": "^7.0.1", + "css-functions-list": "^3.0.1", + "debug": "^4.3.4", + "execall": "^2.0.0", + "fast-glob": "^3.2.11", + "fastest-levenshtein": "^1.0.12", + "file-entry-cache": "^6.0.1", + "get-stdin": "^8.0.0", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.2.0", + "ignore": "^5.2.0", + "import-lazy": "^4.0.0", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.25.0", + "mathml-tag-names": "^2.1.3", + "meow": "^9.0.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "normalize-selector": "^0.2.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.13", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^6.0.0", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "specificity": "^0.4.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "style-search": "^0.1.0", + "supports-hyperlinks": "^2.2.0", + "svg-tags": "^1.0.0", + "table": "^6.8.0", + "v8-compile-cache": "^2.3.0", + "write-file-atomic": "^4.0.1" + }, + "bin": { + "stylelint": "bin/stylelint.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + } + }, + "node_modules/stylelint-config-html": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.0.0.tgz", + "integrity": "sha512-rKQUUWDpaYC7ybsS6tLxddjn6DxhjSIXybElSmcTyVQj3ExhmU3q+l41ktrlwHRyY0M5SkTkZiwngvYPYmsgSQ==", + "dev": true, + "engines": { + "node": "^12 || >=14" + }, + "peerDependencies": { + "postcss-html": "^1.0.0", + "stylelint": ">=14.0.0" + } + }, + "node_modules/stylelint-config-recommended": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", + "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", + "dev": true, + "peerDependencies": { + "stylelint": "^14.4.0" + } + }, + "node_modules/stylelint-config-recommended-vue": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-vue/-/stylelint-config-recommended-vue-1.4.0.tgz", + "integrity": "sha512-DVJqyX2KvMCn9U0+keL12r7xlsH26K4Vg8NrIZuq5MoF7g82DpMp326Om4E0Q+Il1o+bTHuUyejf2XAI0iD04Q==", + "dev": true, + "dependencies": { + "semver": "^7.3.5", + "stylelint-config-html": ">=1.0.0", + "stylelint-config-recommended": ">=6.0.0" + }, + "engines": { + "node": "^12 || >=14" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "postcss-html": "^1.0.0", + "stylelint": ">=14.0.0" + } + }, + "node_modules/stylelint-config-recommended-vue/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stylelint-config-recommended-vue/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stylelint-config-recommended-vue/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/stylelint-config-standard": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-25.0.0.tgz", + "integrity": "sha512-21HnP3VSpaT1wFjFvv9VjvOGDtAviv47uTp3uFmzcN+3Lt+RYRv6oAplLaV51Kf792JSxJ6svCJh/G18E9VnCA==", + "dev": true, + "dependencies": { + "stylelint-config-recommended": "^7.0.0" + }, + "peerDependencies": { + "stylelint": "^14.4.0" + } + }, + "node_modules/stylelint-order": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-5.0.0.tgz", + "integrity": "sha512-OWQ7pmicXufDw5BlRqzdz3fkGKJPgLyDwD1rFY3AIEfIH/LQY38Vu/85v8/up0I+VPiuGRwbc2Hg3zLAsJaiyw==", + "dev": true, + "dependencies": { + "postcss": "^8.3.11", + "postcss-sorting": "^7.0.1" + }, + "peerDependencies": { + "stylelint": "^14.0.0" + } + }, + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "node_modules/stylelint/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/stylelint/node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/stylelint/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylelint/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/stylelint/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -14097,6 +14740,52 @@ "node": ">=4" } }, + "node_modules/supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/svg-pan-zoom": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/svg-pan-zoom/-/svg-pan-zoom-3.6.1.tgz", @@ -14139,9 +14828,9 @@ } }, "node_modules/table": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.3.tgz", - "integrity": "sha512-5DkIxeA7XERBqMwJq0aHZOdMadBx4e6eDoFRuyT5VR82J0Ycg2DwM6GfA/EQAhJ+toRTaS1lIdSQCqgrmhPnlw==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", + "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", "dev": true, "dependencies": { "ajv": "^8.0.1", @@ -14394,6 +15083,18 @@ "node": ">=4" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -14417,6 +15118,39 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/tslib": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", @@ -14462,6 +15196,21 @@ "node": ">= 0.6" } }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -14976,6 +15725,18 @@ "node": ">=8" } }, + "node_modules/vue-loading-overlay": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/vue-loading-overlay/-/vue-loading-overlay-3.4.2.tgz", + "integrity": "sha512-xcB+NPjl76eA0uggm707x3ZFgrNosZXpynHipyS3K+rrK1NztOV49R1LY+/4ij5W1KYANp7eRI2EIHrxCpmWAw==", + "engines": { + "node": ">=6.9.0", + "npm": ">=3.10.0" + }, + "peerDependencies": { + "vue": "^2.0.0" + } + }, "node_modules/vue-resize": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-1.0.1.tgz", @@ -15507,6 +16268,22 @@ "which": "bin/which" } }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/wildcard": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", @@ -15573,6 +16350,19 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "node_modules/write-file-atomic": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", + "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, "node_modules/ws": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", @@ -15707,9 +16497,9 @@ }, "dependencies": { "@antimatter-dimensions/notations": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@antimatter-dimensions/notations/-/notations-2.2.0.tgz", - "integrity": "sha512-EGXWMQer0Bvb1kujon6rvZdj+3k533SlfLtgoz+y35hMhsvQNoduURpZ0j+apFSpC0vpeEvVUjXqq6eUmQrJZQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@antimatter-dimensions/notations/-/notations-3.0.3.tgz", + "integrity": "sha512-ldCH7P0G8saC1nE8D04hRZ3tUVjQ2YHkIBKowAfyTzZEppZtxpR6Zl+GqBE9lhT1Jv+no0xIKWm3QttQ5qODLg==", "requires": { "break_infinity.js": "^1.2.0", "tslib": "^2.0.0" @@ -17834,6 +18624,12 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, "@types/long": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", @@ -19416,12 +20212,6 @@ "whatwg-fetch": "^3.6.2" }, "dependencies": { - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, "@vue/cli-shared-utils": { "version": "5.0.0-rc.0", "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.0-rc.0.tgz", @@ -19586,21 +20376,6 @@ "color-convert": "^2.0.1" } }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -19644,15 +20419,6 @@ "ms": "2.1.2" } }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, "enhanced-resolve": { "version": "5.8.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", @@ -19673,66 +20439,18 @@ "estraverse": "^4.1.1" } }, - "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, "glob-to-regexp": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, - "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, "loader-runner": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", @@ -19769,16 +20487,6 @@ "yallist": "^4.0.0" } }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -19828,12 +20536,6 @@ "wcwidth": "^1.0.1" } }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -19873,12 +20575,6 @@ "randombytes": "^2.1.0" } }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -19954,15 +20650,6 @@ "schema-utils": "^3.0.0" } }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, "watchpack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz", @@ -20500,12 +21187,43 @@ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", "dev": true }, + "array-includes": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + } + }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "array.prototype.flat": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -20730,6 +21448,15 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "break_infinity.js": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/break_infinity.js/-/break_infinity.js-1.3.0.tgz", @@ -20888,6 +21615,25 @@ "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + } + } + }, "caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -20986,41 +21732,6 @@ "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } } }, "chrome-trace-event": { @@ -21176,6 +21887,15 @@ "shallow-clone": "^3.0.0" } }, + "clone-regexp": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", + "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", + "dev": true, + "requires": { + "is-regexp": "^2.0.0" + } + }, "codemirror": { "version": "5.65.1", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.1.tgz", @@ -21197,9 +21917,9 @@ "dev": true }, "colord": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.1.tgz", - "integrity": "sha512-4LBMSt09vR0uLnPVkOUBnmxgoaeN4ewRbx801wY/bXcltXfpR/G46OdWn96XpYmCWuYvO46aBZP4NgX8HpNAcw==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", + "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", "dev": true }, "colorette": { @@ -21319,69 +22039,6 @@ "serialize-javascript": "^6.0.0" }, "dependencies": { - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, "glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -21391,48 +22048,6 @@ "is-glob": "^4.0.3" } }, - "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, - "ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, "schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", @@ -21452,21 +22067,6 @@ "requires": { "randombytes": "^2.1.0" } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } } } }, @@ -21547,6 +22147,12 @@ "timsort": "^0.3.0" } }, + "css-functions-list": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.0.1.tgz", + "integrity": "sha512-PriDuifDt4u4rkDgnqRCLnjfMatufLmWNfQnGCq34xZwpY3oabwhB9SqRBmuvWUgndbemCFlKqg+nO7C2q0SBw==", + "dev": true + }, "css-loader": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", @@ -21754,6 +22360,30 @@ "ms": "2.0.0" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + } + } + }, "deep-equal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", @@ -21901,12 +22531,13 @@ "dev": true }, "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "dev": true, "requires": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" } }, "del": { @@ -21925,100 +22556,6 @@ "slash": "^3.0.0" }, "dependencies": { - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, - "ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -22033,15 +22570,6 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } } } }, @@ -22290,12 +22818,63 @@ "stackframe": "^1.1.1" } }, + "es-abstract": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.0.tgz", + "integrity": "sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.1", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } + }, "es-module-lexer": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", "dev": true }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -22532,6 +23111,148 @@ } } }, + "eslint-import-resolver-alias": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz", + "integrity": "sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==", + "dev": true, + "requires": {} + }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "dev": true, + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", + "has": "^1.0.3", + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + } + } + }, "eslint-plugin-vue": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.0.3.tgz", @@ -22625,40 +23346,6 @@ "schema-utils": "^3.1.1" }, "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, "schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", @@ -22669,15 +23356,6 @@ "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } } } }, @@ -22797,6 +23475,15 @@ "strip-eof": "^1.0.0" } }, + "execall": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", + "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", + "dev": true, + "requires": { + "clone-regexp": "^2.1.0" + } + }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -22856,9 +23543,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -22866,51 +23553,6 @@ "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } } }, "fast-json-stable-stringify": { @@ -22925,6 +23567,12 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -22951,6 +23599,15 @@ "flat-cache": "^3.0.4" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -23107,12 +23764,30 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, "gamma": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gamma/-/gamma-1.0.0.tgz", @@ -23140,6 +23815,12 @@ "has-symbols": "^1.0.1" } }, + "get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true + }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -23149,6 +23830,16 @@ "pump": "^3.0.0" } }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -23177,6 +23868,26 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -23184,27 +23895,33 @@ "dev": true }, "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "dependencies": { "ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true } } }, + "globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", + "dev": true + }, "graceful-fs": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", @@ -23226,6 +23943,12 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -23235,16 +23958,31 @@ "function-bind": "^1.1.1" } }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true }, "has-tostringtag": { @@ -23347,9 +24085,9 @@ } }, "html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", "dev": true }, "html-webpack-plugin": { @@ -23439,51 +24177,6 @@ "is-glob": "^4.0.1", "is-plain-obj": "^3.0.0", "micromatch": "^4.0.2" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } } }, "human-signals": { @@ -23540,6 +24233,12 @@ "resolve-from": "^4.0.0" } }, + "import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -23566,6 +24265,12 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, "internal-ip": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-6.2.0.tgz", @@ -23586,6 +24291,17 @@ } } }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -23626,6 +24342,15 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -23635,6 +24360,22 @@ "binary-extensions": "^2.0.0" } }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, "is-ci": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", @@ -23645,9 +24386,9 @@ } }, "is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "dev": true, "requires": { "has": "^1.0.3" @@ -23712,6 +24453,27 @@ "ip-regex": "^4.0.0" } }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -23749,24 +24511,66 @@ "has-tostringtag": "^1.0.0" } }, + "is-regexp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", + "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", + "dev": true + }, "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, "is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, "is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -23977,6 +24781,12 @@ "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", "dev": true }, + "known-css-properties": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz", + "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==", + "dev": true + }, "launch-editor": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.2.1.tgz", @@ -24296,6 +25106,18 @@ } } }, + "map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true + }, + "mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true + }, "mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -24317,6 +25139,79 @@ "fs-monkey": "1.0.3" } }, + "meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "dependencies": { + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -24358,6 +25253,16 @@ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", "dev": true }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, "mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -24385,6 +25290,12 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, "mini-css-extract-plugin": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.4.tgz", @@ -24414,19 +25325,38 @@ "dev": true }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "dependencies": { + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + } + } + }, "minipass": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", @@ -24503,9 +25433,9 @@ } }, "nanoid": { - "version": "3.1.30", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", - "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", "dev": true }, "natural-compare": { @@ -24606,6 +25536,12 @@ "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", "dev": true }, + "normalize-selector": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/normalize-selector/-/normalize-selector-0.2.0.tgz", + "integrity": "sha1-0LFF62kRicY6eNIB3E/bEpPvDAM=", + "dev": true + }, "normalize-url": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", @@ -24636,6 +25572,12 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true + }, "object-is": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", @@ -24664,6 +25606,17 @@ "object-keys": "^1.1.1" } }, + "object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, "obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -25054,9 +26007,9 @@ "dev": true }, "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pkg-dir": { @@ -25102,14 +26055,14 @@ } }, "postcss": { - "version": "8.3.11", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", - "integrity": "sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==", + "version": "8.4.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.13.tgz", + "integrity": "sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==", "dev": true, "requires": { - "nanoid": "^3.1.30", + "nanoid": "^3.3.3", "picocolors": "^1.0.0", - "source-map-js": "^0.6.2" + "source-map-js": "^1.0.2" } }, "postcss-calc": { @@ -25171,6 +26124,37 @@ "dev": true, "requires": {} }, + "postcss-html": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.4.1.tgz", + "integrity": "sha512-OKihuWxPuBQrQeLNsavP7ytJ9IYNj/ViAXB2v7Qjh56LnfESKrkahKA9si4VfPN8xtz6oqUE6KdL0bTPrHJr6g==", + "dev": true, + "requires": { + "htmlparser2": "^7.1.2", + "postcss": "^8.4.0", + "postcss-safe-parser": "^6.0.0" + }, + "dependencies": { + "entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "dev": true + }, + "htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + } + } + }, "postcss-loader": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.0.tgz", @@ -25208,6 +26192,12 @@ } } }, + "postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", + "dev": true + }, "postcss-merge-longhand": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.3.tgz", @@ -25426,16 +26416,36 @@ "postcss-value-parser": "^4.1.0" } }, + "postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", + "dev": true + }, + "postcss-safe-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "dev": true, + "requires": {} + }, "postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", "dev": true, "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, + "postcss-sorting": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-7.0.1.tgz", + "integrity": "sha512-iLBFYz6VRYyLJEJsBJ8M3TCqNcckVzz4wFounSc5Oez35ogE/X+aoC5fFu103Ot7NyvjU3/xqIXn93Gp3kJk4g==", + "dev": true, + "requires": {} + }, "postcss-svgo": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.3.tgz", @@ -25458,9 +26468,9 @@ } }, "postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, "prelude-ls": { @@ -25596,6 +26606,12 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -25685,6 +26701,27 @@ "picomatch": "^2.2.1" } }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "dependencies": { + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + } + } + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -25720,13 +26757,14 @@ "integrity": "sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw==" }, "regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" } }, "regexpp": { @@ -25809,13 +26847,14 @@ "dev": true }, "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "dev": true, "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "resolve-from": { @@ -26055,10 +27094,21 @@ "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", "dev": true }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "sirv": { @@ -26146,9 +27196,9 @@ "dev": true }, "source-map-js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "dev": true }, "source-map-support": { @@ -26273,6 +27323,12 @@ } } }, + "specificity": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", + "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", + "dev": true + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -26315,6 +27371,28 @@ "strip-ansi": "^6.0.1" } }, + "string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -26323,6 +27401,12 @@ "ansi-regex": "^5.0.1" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -26347,6 +27431,12 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=", + "dev": true + }, "stylehacks": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.1.tgz", @@ -26357,6 +27447,166 @@ "postcss-selector-parser": "^6.0.4" } }, + "stylelint": { + "version": "14.8.2", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.8.2.tgz", + "integrity": "sha512-tjDfexCYfoPdl/xcDJ9Fv+Ko9cvzbDnmdiaqEn3ovXHXasi/hbkt5tSjsiReQ+ENqnz0eltaX/AOO+AlzVdcNA==", + "dev": true, + "requires": { + "balanced-match": "^2.0.0", + "colord": "^2.9.2", + "cosmiconfig": "^7.0.1", + "css-functions-list": "^3.0.1", + "debug": "^4.3.4", + "execall": "^2.0.0", + "fast-glob": "^3.2.11", + "fastest-levenshtein": "^1.0.12", + "file-entry-cache": "^6.0.1", + "get-stdin": "^8.0.0", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.2.0", + "ignore": "^5.2.0", + "import-lazy": "^4.0.0", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.25.0", + "mathml-tag-names": "^2.1.3", + "meow": "^9.0.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "normalize-selector": "^0.2.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.13", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^6.0.0", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "specificity": "^0.4.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "style-search": "^0.1.0", + "supports-hyperlinks": "^2.2.0", + "svg-tags": "^1.0.0", + "table": "^6.8.0", + "v8-compile-cache": "^2.3.0", + "write-file-atomic": "^4.0.1" + }, + "dependencies": { + "balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "stylelint-config-html": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.0.0.tgz", + "integrity": "sha512-rKQUUWDpaYC7ybsS6tLxddjn6DxhjSIXybElSmcTyVQj3ExhmU3q+l41ktrlwHRyY0M5SkTkZiwngvYPYmsgSQ==", + "dev": true, + "requires": {} + }, + "stylelint-config-recommended": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", + "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", + "dev": true, + "requires": {} + }, + "stylelint-config-recommended-vue": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-vue/-/stylelint-config-recommended-vue-1.4.0.tgz", + "integrity": "sha512-DVJqyX2KvMCn9U0+keL12r7xlsH26K4Vg8NrIZuq5MoF7g82DpMp326Om4E0Q+Il1o+bTHuUyejf2XAI0iD04Q==", + "dev": true, + "requires": { + "semver": "^7.3.5", + "stylelint-config-html": ">=1.0.0", + "stylelint-config-recommended": ">=6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "stylelint-config-standard": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-25.0.0.tgz", + "integrity": "sha512-21HnP3VSpaT1wFjFvv9VjvOGDtAviv47uTp3uFmzcN+3Lt+RYRv6oAplLaV51Kf792JSxJ6svCJh/G18E9VnCA==", + "dev": true, + "requires": { + "stylelint-config-recommended": "^7.0.0" + } + }, + "stylelint-order": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-5.0.0.tgz", + "integrity": "sha512-OWQ7pmicXufDw5BlRqzdz3fkGKJPgLyDwD1rFY3AIEfIH/LQY38Vu/85v8/up0I+VPiuGRwbc2Hg3zLAsJaiyw==", + "dev": true, + "requires": { + "postcss": "^8.3.11", + "postcss-sorting": "^7.0.1" + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -26366,6 +27616,39 @@ "has-flag": "^3.0.0" } }, + "supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, "svg-pan-zoom": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/svg-pan-zoom/-/svg-pan-zoom-3.6.1.tgz", @@ -26401,9 +27684,9 @@ } }, "table": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.3.tgz", - "integrity": "sha512-5DkIxeA7XERBqMwJq0aHZOdMadBx4e6eDoFRuyT5VR82J0Ycg2DwM6GfA/EQAhJ+toRTaS1lIdSQCqgrmhPnlw==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", + "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", "dev": true, "requires": { "ajv": "^8.0.1", @@ -26582,6 +27865,15 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -26599,6 +27891,35 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, + "trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true + }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, "tslib": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", @@ -26634,6 +27955,18 @@ "mime-types": "~2.1.24" } }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -27022,6 +28355,12 @@ } } }, + "vue-loading-overlay": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/vue-loading-overlay/-/vue-loading-overlay-3.4.2.tgz", + "integrity": "sha512-xcB+NPjl76eA0uggm707x3ZFgrNosZXpynHipyS3K+rrK1NztOV49R1LY+/4ij5W1KYANp7eRI2EIHrxCpmWAw==", + "requires": {} + }, "vue-resize": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-1.0.1.tgz", @@ -27411,6 +28750,19 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "wildcard": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", @@ -27461,6 +28813,16 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "write-file-atomic": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", + "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, "ws": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", diff --git a/package.json b/package.json index 9624c9650..eecdfbc08 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "lint": "vue-cli-service lint" }, "dependencies": { - "@antimatter-dimensions/notations": "^2.2.0", + "@antimatter-dimensions/notations": "^3.0.3", "@fortawesome/fontawesome-free": "^6.1.1", "break_infinity.js": "^1.3.0", "chevrotain": "^4.8.1", @@ -27,6 +27,7 @@ "vis-util": "^4.0.0", "vue": "^2.6.11", "vue-gtag": "^1.16.1", + "vue-loading-overlay": "^3.4.2", "vue-splitpane": "^1.0.6", "vuedraggable": "^2.24.3" }, @@ -38,7 +39,14 @@ "@vue/cli-service": "^5.0.0-rc.0", "browserslist-useragent-regexp": "^3.0.2", "eslint": "^7.20.0", + "eslint-import-resolver-alias": "^1.1.2", + "eslint-plugin-import": "^2.26.0", "eslint-plugin-vue": "^8.0.3", + "postcss-html": "^1.4.1", + "stylelint": "^14.8.2", + "stylelint-config-recommended-vue": "^1.4.0", + "stylelint-config-standard": "^25.0.0", + "stylelint-order": "^5.0.0", "vue-template-compiler": "^2.6.11", "webpack": "^5.64.0" } diff --git a/public/changelog.html b/public/changelog.html deleted file mode 100644 index 4b760031c..000000000 --- a/public/changelog.html +++ /dev/null @@ -1,422 +0,0 @@ - - - - - - Changelog - - -
-

Changelog

-
-

-
- 17.6.2018 -
-
-

“This update sucks” -update

-
- Major stuff:
- TIME DILATION
- 3 ROWS OF SECRET ACHIEVEMENTS
- Added more Nicholas Cage.
- 1 new row of achievements.
- Added 3 study tree save slots.
- Greatly improved performance. (up to 5x in certain cases, ~3x in almost all cases)
- Nerfed EC10 reward. ((infinities * EC10 completions * 0.000002+1) > (infinities ^ 0.9 * EC10 completions * 0.000002+1))
- Added even more Nicholas Cage.
- Time study 11 has been capped at 1e2500 and now displays its current multiplier.
- Time study 193 has been buffed, requires ~1012680 eternities to cap, rather than 1.5m, and is now capped at 1e13000 instead of ~1.81e12900/1.5m eternities. (1.02^x) > (1.03^x)
- The second eternity upgrade has been buffed, and now soft caps at 100k, rather than 125k. The end result is very slightly higher. ((x/300)^log4(2x)) > ((x/200)^log4(2x))
- EC1 now requires 20k eternities per tier to unlock, down from 25k.
- TD cost scaling has been increased after costs of 1e1300. (this is in addition to the current increase)
- Added additional galaxy cost scaling after 800 galaxies.
- Added a button to buy the maximum amount of eternity point multipliers at once.
- Offline progress processes ~5x faster, and now simulates autobuyers. (please note that offline progress is still capped at 1000 ticks, with additional ticks increasing the production of said 1000 ticks)
- Added a new save file system that allows 3 different save files at once all with cloud save enabled, along with a new cloud save UI.
- Added an animation to visualize your multiplier gain when you purchase 10 of a dimension, dimension boost/shift, or sacrifice.
- Nicholas Cage.
- Added an animation to big crunches. This will only trigger if you haven’t eternitied, have a fastest infinity time above 1 minute, and haven’t broken infinity.
- Added a button in the options menu to disable individual animations.
- Added more news ticker entries

- Minor stuff:
- Reduced the space between the secondary eternity tab buttons.
- The EC3 description now specifies that dimensional sacrifice is disabled.
- Autobuyer inputs now support commas and notation on exponents.
- When purchasing the EP or IP multipliers, autobuyer inputs will now always format the updated value above 1000.
- The size and placement of the auto IP multiplier and auto RG toggles have been adjusted to fit with the other auto toggles.
- Total time played now increases at a normal rate inside EC12.
- Fastest infinity time now updates normally inside EC12.
- The time theorem purchasing background is now 20 pixels wider.
- Changed the wording on time study 133 for clarity.
- Added various missing periods to achievements.
- Improved chart performance. (it’s still pretty laggy if your settings are too high)
- You can now purchase study 201 while you have EC11/12 bought, but you cannot purchase another path.
- Purchasing study 131 no longer turns off your replicanti galaxy autobuyer, but instead displays it as disabled.
- You can now purchase another split using shift while you have study 201.
- You now purchase max galaxies manually by clicking or using the hotkey with more than 6 eternities.
- You can now purchase single dimension boosts and galaxies by holding shift while purchasing.
- ID8 will now display a rate of change after completing EC7 at least once.
- Added an oxford comma to formatted time values.
- Made the dimensional sacrifice button 40px wider to prevent the text overflowing.
- Made the all tab eternity and infinity point displays 30px wider to prevent the text overflowing.
- Moved the big crunch button up to prevent blocking the statistics and achievement tab buttons.
- Moved the eternity and infinity buttons inwards to prevent the HTML layout jumping around.
- Fixed the placement of certain footers.
- Fixed a typo where a news ticker said "Dimesional Sacrifice" instead of "Dimensional Sacrifice"
- Fixed a bug where TDs displayed a 2x multiplier per purchase when they actually gave a 4x multiplier.
- Fixed a bug where study 51 wouldn't respect notation.
- Fixed a bug where the infinity challenges tab would always show.
- Fixed a bug where the auto RG toggle would jiggle left and right 1 pixel in certain cases.
- Fixed a bug where the rate of change on the 7th dimension wouldn't take into account ID1 while in EC7.
- Fixed a bug where EC12 displayed 0.1 seconds after 5 completions, but actually required 0.0 seconds.
- Fixed a bug where tickspeed elements wouldn’t hide correctly in certain cases.
- Fixed a bug where bought eternity challenge unlock studies would show as gray in the dark theme rather than a deep purple.
- Fixed a bug where dimensions 5-8 would hide upon eternity even with the 30 eternity milestone.
- Fixed a bug where popup colors weren’t inverted in the inverted and inverted metro themes.
- Fixed a bug where the eternity point amount wouldn’t show when you imported a save with eternity points into a save without them.
- Fixed a bug where locked eternity challenges didn’t have a hover effect in the dark metro theme.
- Fixed a bug where popups weren’t properly centered.
- Fixed a bug where ID autobuyers would purchase IDs upon unlock even while disabled.
- Fixed a bug where study tree branches drawn to row 22 were off-centered.
- Fixed a bug where EP/min and IP/min peaks wouldn’t update properly upon import.
- Fixed a bug where infinity dimension autobuyers wouldn’t hide properly upon import.
- Fixed a bug where the IP multiplier autobuyer wouldn’t hide properly upon import.
- Fixed a bug where the option to change big crunch modes wouldn’t hide properly upon import.
- Fixed a bug where the max buy galaxy interval setting wouldn’t hide properly upon import.
- Fixed a bug where the RG autobuyer wouldn’t hide properly upon import.
- Fixed a bug where the eternity confirmation option wouldn’t hide properly upon import.
- Fixed a bug where the replicanti upgrade autobuyers wouldn’t hide properly upon import.
- Fixed a bug where your update rate wouldn’t update upon import.
- Fixed a bug where the chart line color wouldn’t update properly upon import.
- Fixed a bug where achievement images were being cut off by 4 pixels on the right and bottom sides.
- Fixed a bug where "Yo dawg, I heard you liked infinities..." only required 1e300 times the previous infinity.
- Fixed a bug where the auto sacrifice interval would still display as 0.10 seconds even with the double autobuyer speed breaking infinity upgrade.
- Fixed a bug where certain time studies were 1 pixel too far to the left or right.
- Fixed a bug where studies 223 & 224 weren’t taken into account when displaying antimatter galaxies as distant antimatter galaxies.
- Fixed a bug where study 227 would multiply your 4th time dimension production by 0 if you had no sacrifice bonus.
- Fixed a bug where the game would say “You have 1 eternity points.” rather than “You have 1 eternity point.”.
- Fixed a bug where popups would remain open after changing tabs.
- Fixed a bug where you were able to select the achievement images by clicking and dragging over them.
- Fixed a bug where studies 233 and 234 had the wrong classes assigned to them on load.
-
-
-
- 1.4.2018 -
-
- Antimatter dimensions "Fixed a bug where there wasn't an update." update patch notes:
- Huge thanks to Omsi for helping me a ton with this.
- MAJOR STUFF:
- 2 NEW ETERNITY CHALLENGES
- 12 NEW TIME STUDIES
- Time study 132 has been buffed from a 30% bonus to a 40% bonus.
- Added an achievement bonus for "Popular music": "Replicanti galaxies divide your replicanti by 1.79e308 instead of resetting them to 1."
- Added an achievement bonus for "IT'S OVER 9000": "Sacrifice doesn't reset your dimensions."
- Added an achievement bonus for "Like feasting on a behind": "IP multiplier based on time spent this infinity."
- Added an achievement bonus for "What do I have to do to get rid of you": "Time dimensions are multiplied by the number of studies you have."
- Added "Infinity" notation.
- Added "Brackets" notation.
- Added an import/export system for the time study tree.
- Added an EP/min & peak EP/min display to the eternity button.
- Added an eternity hotkey.
- Added something to help you pick your theme.
- Added a few more IAPs.
- Reduced the cost of "Double IP gain from all sources" IAP from 50 ➜ 40


- Minor stuff:
- Added an option to not plot drops in production on the chart. (it will instead copy the newest data point)
- Added displays for the current bonuses from time studies 71, 72, and 73.
- Built up speed for 6 hours to do it in 0.5x A presses.
- Changed study 72 to only work on the 4th infinity dimension, but doubled its power. (no effective change)
- Alchemy 120 (Vivification) scaling decreased.
- Fixed a bug where the buttons to purchase time studies wouldn't move in inverted themes on firefox.
- Fixed a bug. Antman, you're good to go.
- Fixed a bug that gave you the ability to set a custom name for your theme when using a secret theme.
- Fixed BLJ. Shoutout to SimpleFlips.
- Fixed a bug that caused purchasing the EP multiplier to require multiple clicks.
- Removed the ghost from the game. Was annoying.
- Fixed a bug that allowed you to earn "Long lasting relationship" in EC7.
- Monkeys no longer eat humans, as intended.
- Fixed a bug where the reward from EC7 could display -1.
- Increased the drop rate of collector's pendant items by 20%.
- Fixed a bug where the infinity requirement for EC4 could be less than 0.
- Transcension gives less Ancient Souls.
- Fixed a bug where the visual display for autobuyer bulk buy settings wouldn't update upon your first eternity.
- Fixed the rickroll. Now it's properly not working.
- Fixed a bug where the EP multiplier would break if its power exceeded 1.79e308.
- Leeroy Jenkins' Battlecry now doesn't trigger Patches.
- Fixed a bug where the confirmation for starting an infinity challenge would say you need to reach infinity.
- Cursors now do circles around the cookie.
- Fixed a bug where the offline progress popup would simply say "While you were away" if nothing happened.
- Traction has been slightly increased to reduce unwanted drifts.
- Fixed a bug that in rare cases would cause the offline progress popup to say you gained "NaNeInfinity" time shards or infinity power.
- Fixed a bug where the tickspeed visual display wouldn't update upon any form of reset.
- Bugged a fix where eternity was dumb.
- CS now makes notes go faster in mania.
- Fixed a bug where replicanti were hidden but still unlocked if you eternitied for the 50th time while they were locked.
- Dirt is now more abundant.
- Fixed a bug where the 1st dimension wasn't producing the 0th dimension.
- Fixed a bug where The Nameless King was too hard.
- Fixed a bug where in a specific case, 2 eternity challenges would appear as running at the same time.
- Increased TukkunFCG YC rewards by 15%.
- Added more space. SPAAAAAAACE
- Fixed a bug where the eternity challenges tab would hide after refreshing with less than 1e2000 antimatter.
- Fixed a bug where eternity challenges wouldn't update correctly upon import.
- Fixed a bug where dimension display values wouldn't update in certain cases.
- Portals are now not red.
- Fixed a bug where the ON/OFF text on the challenge confirmation option wasn't capitalized upon load.
- Reduced GRB's autokill threshold to 2500/2000 power/toughness.
- Fixed a typo where the eternity confirmation option said "Eternity confimation".
- Added bugs because Omsi wants more bugs to fix. Absolute legend, I'm telling you, the queen is legendary.
- Fixed a typo where the reward for "That's faster!" said you started with 20000 antimatter, rather than 200000.
- Added depression to your themes.
- Fixed inconsistencies with the standard notation naming convention.
- Tried to fix a bug where the game was bad but failed. The game is still bad.
- Changed the wording on EC4 to say "X or less" rather than "less than X". (it always worked this way, this is just a correction)
- Made donkeys less fast, so you can actually catch them now.
- Changed the wording on the EC2 reward to say "affects 1st Infinity Dimension" rather than "affects Infinity Dimensions". (it always worked this way, this is just a correction)
- Increased the base breeding speed of trimps by 10%.
- You can now click through the footer and progress bar to access buttons that they are overlapping. (this is for smaller screens)
- Made periods longer.
- Added loot boxes.
- Removed loot boxes.
- Added various missing periods to achievement descriptions.
- Added a missing period to time spent in this eternity.
- Increased the price of creation count increases from 50 god power to 60.
- Added a missing space to the "Autobuyers work twice as fast." upgrade.
- Manually buying max dimension boosts no longer requires 10 eternities or more, and now only requires the bulk buy dimension boosts breaking infinity upgrade.
- Did a barrel roll.
- Added more useless patch notes -
-


-
1.2.2018
-
- ETERNITY CHALLENGES
- NEW TIME STUDIES
- 2 new achievement rows
- Made certain news messages only show if you have reached certain levels of progression
- Massively improved performance of calculating dimension costs thanks to SpectralFlame (cuts cpu usage by up to 2/3 in late-game)
- New news (get it?) ticker entries
- Added a production chart
- Added new statistics to replace the scale statistic after 1e100000 antimatter
- Added a new milestone for 30 eternities: "Start with all normal dimensions available for purchase".
- Added an option to change the update rate of the game, ranging from 33ms to 200ms. (before this, it was locked at 50ms)
- The game now partially simulates offline progress, instead of estimating it.
- Added 3 new eternity upgrades.
- Added a reward to the "NEW DIMENSIONS???" achievement, "Your achievement bonus affects Infinity Dimensions."
- Buffed time study 111. (10 ^ (log10(antimatter) / 290 - 0.75)) > (10 ^ (log10(antimatter) / 285 - 0.75))
- Buffed time study 83. (1.0001^x) > (1.0004^x)
- Nerfed eternity upgrade 1. ((x+1)^3) > (x+1)
- Nerfed eternity upgrade 2. (x^log4(2x)) > ((x/300)^log4(2x) with harsher formula above 125,000)
- Fixed a bunch of bugs and changed a bunch of things. (more detail below)
- Added buy max buttons to Time Dimensions and Time Theorems.
- Added a hotkey for replicanti galaxies. (R)

- Nitty gritty:
- Greatly improved the performance of calculating bonus tickspeed from time dimensions.
- Replaced all references to soft resets with references to dimension boosts.
- Made achievements update on import/hardreset.
- Made the game take into account your infinity points gained on crunch for the purposes of eternity point gain when you eternity
- The replicanti interval is now displayed after and reductions / increases.
- Added missing periods to various achievements.
- Made the bonus from time study 131 display next to max replicanti galaxies.
- Added time dimensions to the info scale.
- Changed the description of time study 31 to "Powers up bonuses that are based on your infinitied stat (to the power of 4)" from "Powers up existing upgrades based on infinitied stat (to the power of 4)"
- Changed the description of "MAXIMUM OVERDRIVE" to say "Big Crunch with X" instead of "Reach X".
- Added "with reduced effect" to the description of time study 71, 72, and 73.
- Changed the text on autobuy max dimension boosts to "Buy max dimboosts every X seconds:" from "Max dimboost interval:". (to achieve parity with the autobuy max galaxies text)
- Made the challenges button always show if you have more than 1 eternity.
- Fixed centering issues with infinity and eternity upgrades.
- Various minor changes to themes to improve consistency. (too minute to list, even here)
- Made the eternity autobuyer number multiply by 5 when you buy the eternity point multiplier.
- Increased the requirement for "Is This Hell?". (5 > 6.66 seconds)
- Reduced the starting replicanti interval upgrade cost. (1e160 > 1e140)
- Galaxies are labeled "Distant Antimatter Galaxies" when the cost scaling starts. (at 100 galaxies)
- Dimensions no longer produce anything after reaching challenge goal, or after reaching infinity with fixed infinity. This is due to the c6 being abusable.
- Made the 7 and 25 eternity milestones work much faster.
- After unlocking bulk dimboosts, clicking dimension boost or pressing D will buy max dimension boosts.
- Moved fake news, don't you dare to sleep, spreading cancer, and one for each dimension to rows 2, 3, 4, and 7 respectively.
- Added a visual display of how many galaxies/dim boosts you have next to the cost.
- Added an explanation of hotkeys to the options page.
- Made shift+1-8 purchase singular dimensions and shift+T purchase a singular tickspeed upgrade.
- Reworked the display of the buy time theorem buttons.
- The milestones page now has 2 columns.
- Extended support for standard notation to e3e18, and letter/cancer notation (almost) infinitely.
- Added support for standard, letter and logrithm notation in autobuyer inputs.
- Added "in a challenge" to the description of "Zero Deaths".
- Made most large numbers in achievements be listed in your chosen notation.
- Nerfed "Gift From The Gods"'s achievement reward.
- Made purchasing time theorems with EP require at least 1 time dimension.
- First eternity now takes you to the time dimensions tab.
- Time dimension prices now have 2 decimal places.
- Reformatted the tick interval reduction text for very small numbers.
- The game now keeps track of when you automatically do an infinity, and you can passively gain IP based off the IP/min in that run if you go offline (but only if infinity isn't broken).
- Made time study 171 apply retroactively. This was causing an issue with production being much lower than expected when going into a long run on the same run as respeccing.
- Fixed a bug where max all wasn't giving achievements when buying dimensions.
- Fixed a bug where the game wouldn't show the default dimensions tab upon hard resetting.
- Fixed a bug where time dimensions were called "X Dimension" rather than "X Time Dimension".
- Fixed a bug where the replicanti galaxy button would show as locked if you had more than the listed max replicanti galaxies and study 131.
- Fixed a bug where the last ten eternities average said IP/X rather than EP/X.
- Fixed a bug where the big crunch autobuyer said "X times since last crunch" instead of "X times last crunch".
- Fixed a bug where the challenge records display wouldn't update upon import.
- Fixed a bug where hotkeys wouldn't work sometimes.
- Fixed a bug where secret theme names would display as "0" after refreshing.
- Fixed a bug where time studies would move around when your window size was too small.
- Fixed a bug where infinity dimensions would reset when clicking on a challenge and not entering while challenge confirmations were on.
- Fixed a bug where you always had the infinity challenge 1 reward.
- Fixed a bug where eternity milestone classes weren't set correctly upon import.
- Fixed a bug where the eternity autobuyer, sacrifice autobuyer, time dimension tab, and replicanti wasn't hiding correctly upon import.
- Fixed a bug where buy max dim boosts was able to buy 1 too many boosts.
- Fixed a bug where the study tree would be off-centered if the game windows wasn't wide enough.
- Fixed a bug where you could buy factions of dimension boosts with dimension boost bulk buy.
- Fixed a bug where your autobuy max dimension boost interval would set itself to itself if you eternitied while changing it it.
- Fixed a bug where secondary statistic tabs weren't hiding upon import.
- Fixed a bug where replicanti galaxies wouldn't give a bonus if you had less than 3 galaxies.
- Fixed a bug where the dimension boost autobuyer would ignore dimension boost costs until they costed 8th dimensions.
- Fixed a bug where the future shop multipliers were displayed before the x rather than after.
- Fixed a bug where the challenge confirmation button's off and on were lowercase.
- Fixed a bug where the static infinity point display would disappear after eternity.
-
-
-
-
1.12.2017
-
- ETERNITY
- Time studies tree with free respec
- Eternity Milestones with tons of automation
- Eternity upgrades
- TIME DIMENSIONS
- REPLICANTIS
- More themes made by Omsi
- Disable hotkeys option
- Current IP/min post-break
- Infinity Challenge times
- Past 10 eternities
- Lowered IP multiplier cost by 1 Order of magnitude.
- 3 more rows of achievements
- Infinity challenge reward nerfs (1st: 1.5x ➜ 1.3x; 3rd: lowered; 4th: mult^1.1 ➜ mult^1.05)
- More news ticker entries
- Immensely improved performance thanks to break_infinity.js made by Patashu, it replaces decimal.js
- Added LZString for cloud saving purposes.
- Achievement refractoring to reduce save string size made by StrangeTim.
- Commas between exponents option for numbers higher than e100000
- Added logarithm notation
- Made letter and cancer notation last longer.
-
-
-
10.10.2017
-
- Complete refactoring for all upgrade UI
- Minor Upgrade Changes (capping some upgrades)
- Kred shop - 3 paid Upgrades - More upgrades (and upgrade improvements) coming in the future
- 8 new Achievements - Achievement Rewards have also been added.
- Infinity Challenges - additional challenges to do going from Inf Dim 2 to current end game and beyond
- Main Screen UI updates - IP points are now visible everywhere
- Hotkeys - C for Big Crunch, M for Max All, S for Dimensional Sacrifice, D for Dimension Shift/Boost, G for Antimatter Galaxy, Numbers 1-8 for Buy 10 (d1-8), A for Toggle Autobuyers
- Bug Fixes - At least 2, including a percentage buff
-
-
-
25.9.2017
-
- NEW DIMENSIONS?
- Super Secret Post-Infinity Dimensions added. Get more antimatter to find out!
- Post-break double galaxy upgrade nerfed. It now gives 50% more.
- Four new post-break upgrades added.
- Scaling of the dimension cost multiplier increased.
- Eight new achievements added.
- Cloud saving maybe added.
- Refunded Dimension cost increase multiplier and changed the cost
-
-
-
19.9.2017
-
- BREAKING INFINITY UPDATE
- Post infinity content added (Breaking infinity), requires big crunch speed to be maxed
- New upgrade tree pre-breaking, included one upgrade that be taken multiple times to increase infinity point gains
- Eight late game post-breaking upgrades
- Eight new achievements
- Reworked autobuyer prices and times, full refund for all points spent on them
- Autobuyers now can be upgraded beyond .1 seconds, and they also now 'wait' after their interval has passed, instantly buying once able to
- Automatic DimBoosts, Galaxies, and Big Crunches now have an input box
- Unique achievement rewards for multiple achievements
- Zero galaxies now gives 11% tickspeed
- Galaxies past two give diminishing returns, Faster than a Potato made easier to compensate
- Game now updates 20 times a second with increased performance, max autobuyer speed is not impacted
- Autobuyer settings are now saved inbetween sessions
- Monitor scaling issues mainly fixed
- Priority should be working properly
- Big crunch button is now less obtrusive
- Your screen no longer defaults to the dimensions tab when you reach infinity (if you have broken infinity or if your fastest time to reach infinity is less than one minute)
- More statistics have been added such as record challenge times and last ten infinities
- Times below one minute are now kept at two decimal points of precision
- Percentage increase per second for dimensions 1-8 are now kept to two decimal points of precision
- The reset button works better now
-
-
-
7.9.2017
-
- CHALLENGE UPDATE:
- 12 Challenges
- 8 new achievements
- Automation
-
-
-
30.8.2017
-
- Added news on top of the page -
-
-
30.8.2017
-
- Added A multiplier for completing a row of achievements.
- New letter notation option
- Nerfed galaxies from +3% to +2%
-
-
-
29.8.2017
-
- 8 More achievements! -
-
-
28.8.2017
-
- Dimensional Sacrifice button, appears at 5th dimension shift/boost.
- More notations!
- Bar until infinity at the bottom.
- Some UI changes
-
-
-
24.8.2017
-
- Infinity update!
- Now when you get to 1.7e308 antimatter, you reach infinity, you can reset again at infinity, gaining infinity points.
- You can use infinity points for upgrades.
- The game also now runs 6 hours while it is closed.
- In addition there are some graphic updates.
-
-
-
7.5.2016
-
Saves should now FINALLY work properly.
-
-
5.5.2016
-
Slightly smaller text and added a max all button.
-
-
4.5.2016
-
Added save button although game saves every 10 seconds.
-
-
3.5.2016
-
Added export and import options.
-
-
3.5.2016
-
Visual update! And statistics.
-
-
3.5.2016
-
Fixed the bug with costs showing for example 1000 SxTg.
-
-
3.5.2016
-
The game now works offtab.
-
-
3.5.2016
-
Added this changelog, fixed money displaying problem. Added a title to the HTML.
-
- - diff --git a/public/images/cancer achievements.png b/public/images/cancer achievements.png index e69825f76..13f17b493 100644 Binary files a/public/images/cancer achievements.png and b/public/images/cancer achievements.png differ diff --git a/public/images/normal achievements.png b/public/images/normal achievements.png index 7d832e657..9397d4c64 100644 Binary files a/public/images/normal achievements.png and b/public/images/normal achievements.png differ diff --git a/public/images/secret achievements.png b/public/images/secret achievements.png index 8a24c241d..fca61b782 100644 Binary files a/public/images/secret achievements.png and b/public/images/secret achievements.png differ diff --git a/public/stylesheets/MonospaceTypewriter.ttf b/public/stylesheets/MonospaceTypewriter.ttf index 3d4c1e022..dc3ae245a 100644 Binary files a/public/stylesheets/MonospaceTypewriter.ttf and b/public/stylesheets/MonospaceTypewriter.ttf differ diff --git a/public/stylesheets/ad-slider-component.css b/public/stylesheets/ad-slider-component.css index c5bbd6e18..a563f958b 100644 --- a/public/stylesheets/ad-slider-component.css +++ b/public/stylesheets/ad-slider-component.css @@ -28,11 +28,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +:root { + --color-slider-blue: #0075ff; +} + .l-ad-slider { display: flex; + width: 100%; position: relative; align-items: center; - width: 100%; } .l-ad-slider--horizontal { @@ -46,23 +50,21 @@ SOFTWARE. .l-ad-slider__wrap { position: relative; box-sizing: border-box; - user-select: none; -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - -o-user-select: none; + user-select: none; } .l-ad-slider--disabled .l-ad-slider__wrap { - opacity: .5; + opacity: 0.5; cursor: not-allowed; } -.l-ad-slider--has-label .l-ad-slider__bg{ +.l-ad-slider--has-label .l-ad-slider__bg { margin-bottom: 1.5rem; } -.l-ad-slider--has-label.l-ad-slider--horizontal .l-ad-slider__bg, .l-ad-slider--has-label.l-ad-slider--horizontal-reverse .l-ad-slider__bg { +.l-ad-slider--has-label.l-ad-slider--horizontal .l-ad-slider__bg, +.l-ad-slider--has-label.l-ad-slider--horizontal-reverse .l-ad-slider__bg { top: 0.75rem; } @@ -71,54 +73,54 @@ SOFTWARE. } .l-ad-slider__bg { - position: relative; display: block; + position: relative; } .c-ad-slider__bg { - border-radius: 1.5rem; - background-color: #ccc; + background-color: #cccccc; + border-radius: var(--var-border-radius, 1.5rem); } .l-ad-slider__bg::after { - content: ''; - position: absolute; - left: 0; - top: 0; + content: ""; width: 100%; height: 100%; + position: absolute; + top: 0; + left: 0; } .l-ad-slider__process { position: absolute; - transition: all 0s; z-index: 1; + transition: all 0s; } .c-ad-slider__process { - border-radius: 15px; - background-color: #3498db; + background-color: var(--color-slider-blue); + border-radius: var(--var-border-radius, 15px); } -.l-ad-slider__wrap .ad-slider-process.ad-slider-process-draggable { - cursor: pointer; +.l-ad-slider__wrap .ad-slider-process-draggable { z-index: 3; + cursor: pointer; } .l-ad-slider--horizontal .l-ad-slider__process { width: 0; height: 100%; + will-change: width; top: 0; left: 0; - will-change: width; } .l-ad-slider--vertical .l-ad-slider__process { width: 100%; height: 0; + will-change: height; bottom: 0; left: 0; - will-change: height; } .l-ad-slider--horizontal-reverse .l-ad-slider__process { @@ -137,9 +139,9 @@ SOFTWARE. .l-ad-slider__dot { position: absolute; - transition: all 0s; will-change: transform; z-index: 2; + transition: all 0s; } .c-ad-slider__dot { @@ -147,28 +149,34 @@ SOFTWARE. } .l-ad-slider__dot-handle { + display: flex; width: 100%; height: 100%; - display: flex; - align-items: center; justify-content: center; + align-items: center; } .c-ad-slider__dot-handle { - border-radius: 50% !important; - background-color: #fff; - box-shadow: 0.5px 0.5px 2px 1px rgba(0, 0, 0, 0.32); + background-color: #ffffff; + border: 0.1rem solid black; + border-radius: var(--var-border-radius, 50%) !important; + box-shadow: 0.5px 0.5px 2px 1px rgba(0, 0, 0, 32%); + transition-duration: 0.2s; +} + +.c-ad-slider__dot-handle:hover { + background-color: var(--color-slider-blue); } .l-ad-slider__dot--focus .c-ad-slider__dot-handle { - box-shadow: 0 0 2px 1px #3498db; + box-shadow: 0 0 2px 1px var(--color-slider-blue); } -.l-ad-slider__dot---dragging { +.l-ad-slider__dot--dragging { z-index: 5; } -.l-ad-slider__dot---disabled { +.l-ad-slider__dot--disabled { z-index: 4; } @@ -188,24 +196,6 @@ SOFTWARE. top: 0; } -.c-ad-slider__button { - height: 1.6rem; - width: 1.6rem; - display: flex; - justify-content: center; - align-items: center; - font-size: 1rem; - border-radius: 50%; - border: 0.1rem solid var(--color-reality-light); - cursor: pointer; - transition-duration: 0.2s; -} - -.c-ad-slider__button:hover { - color: black; - background-color: var(--color-reality-light); -} - .l-ad-slider__wrap .ad-slider-tooltip-wrap { display: none; position: absolute; @@ -214,15 +204,15 @@ SOFTWARE. .l-ad-slider__wrap .ad-slider-tooltip { display: block; - font-size: 14px; white-space: nowrap; - padding: 2px 5px; min-width: 20px; text-align: center; - color: #fff; - border-radius: 5px; - border: 0.1rem solid #3498db; - background-color: #3498db; + font-size: 14px; + color: #ffffff; + background-color: var(--color-slider-blue); + border: 0.1rem solid var(--color-slider-blue); + border-radius: var(--var-border-radius, 5px); + padding: 2px 5px; } .l-ad-slider__wrap .ad-slider-tooltip-wrap.ad-slider-tooltip-top { @@ -249,13 +239,14 @@ SOFTWARE. transform: translate(100%, -50%); } -.l-ad-slider__wrap .ad-slider-tooltip-wrap.ad-slider-tooltip-top .ad-slider-tooltip::before, .l-ad-slider__wrap .ad-slider-tooltip-top .vue-merged-tooltip .ad-slider-tooltip::before { - content: ''; +.l-ad-slider__wrap .ad-slider-tooltip-wrap.ad-slider-tooltip-top .ad-slider-tooltip::before, +.l-ad-slider__wrap .ad-slider-tooltip-top .vue-merged-tooltip .ad-slider-tooltip::before { + content: ""; + width: 0; + height: 0; position: absolute; bottom: -10px; left: 50%; - width: 0; - height: 0; border: 5px solid transparent; border: 6px solid transparent\0; border-top-color: inherit; @@ -267,39 +258,42 @@ SOFTWARE. visibility: hidden; } -.l-ad-slider__wrap .ad-slider-tooltip-wrap.ad-slider-tooltip-bottom .ad-slider-tooltip::before, .l-ad-slider__wrap .ad-slider-tooltip-bottom .vue-merged-tooltip .ad-slider-tooltip::before { - content: ''; +.l-ad-slider__wrap .ad-slider-tooltip-wrap.ad-slider-tooltip-bottom .ad-slider-tooltip::before, +.l-ad-slider__wrap .ad-slider-tooltip-bottom .vue-merged-tooltip .ad-slider-tooltip::before { + content: ""; + width: 0; + height: 0; position: absolute; top: -10px; left: 50%; - width: 0; - height: 0; border: 5px solid transparent; border: 6px solid transparent\0; border-bottom-color: inherit; transform: translate(-50%, 0); } -.l-ad-slider__wrap .ad-slider-tooltip-wrap.ad-slider-tooltip-left .ad-slider-tooltip::before, .l-ad-slider__wrap .ad-slider-tooltip-left .vue-merged-tooltip .ad-slider-tooltip::before { - content: ''; +.l-ad-slider__wrap .ad-slider-tooltip-wrap.ad-slider-tooltip-left .ad-slider-tooltip::before, +.l-ad-slider__wrap .ad-slider-tooltip-left .vue-merged-tooltip .ad-slider-tooltip::before { + content: ""; + width: 0; + height: 0; position: absolute; top: 50%; right: -10px; - width: 0; - height: 0; border: 5px solid transparent; border: 6px solid transparent\0; border-left-color: inherit; transform: translate(0, -50%); } -.l-ad-slider__wrap .ad-slider-tooltip-wrap.ad-slider-tooltip-right .ad-slider-tooltip::before, .l-ad-slider__wrap .ad-slider-tooltip-right .vue-merged-tooltip .ad-slider-tooltip::before { - content: ''; +.l-ad-slider__wrap .ad-slider-tooltip-wrap.ad-slider-tooltip-right .ad-slider-tooltip::before, +.l-ad-slider__wrap .ad-slider-tooltip-right .vue-merged-tooltip .ad-slider-tooltip::before { + content: ""; + width: 0; + height: 0; position: absolute; top: 50%; left: -10px; - width: 0; - height: 0; border: 5px solid transparent; border: 6px solid transparent\0; border-right-color: inherit; @@ -311,64 +305,67 @@ SOFTWARE. } .l-ad-slider__wrap .l-ad-slider__dot.ad-slider-always .ad-slider-tooltip-wrap { - display: block!important; + display: block !important; } .l-ad-slider__wrap .ad-slider-piecewise { - position: absolute; - width: 100%; - padding: 0; - margin: 0; - left: 0; - top: 0; - height: 100%; list-style: none; + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + margin: 0; + padding: 0; } .l-ad-slider__wrap .ad-slider-piecewise-item { - position: absolute; width: 8px; height: 8px; + position: absolute; } .l-ad-slider__wrap .ad-slider-piecewise-dot { - position: absolute; - left: 50%; - top: 50%; + display: inline-block; width: 100%; height: 100%; - display: inline-block; - background-color: rgba(0, 0, 0, 0.16); - border-radius: 50%; - transform: translate(-50%, -50%); + position: absolute; + top: 50%; + left: 50%; z-index: 2; - transition: all .3s; + background-color: rgba(0, 0, 0, 16%); + border-radius: var(--var-border-radius, 50%); + transform: translate(-50%, -50%); + transition: all 0.3s; } -.l-ad-slider__wrap .ad-slider-piecewise-item:first-child .ad-slider-piecewise-dot, .l-ad-slider__wrap .ad-slider-piecewise-item:last-child .ad-slider-piecewise-dot { +.l-ad-slider__wrap .ad-slider-piecewise-item:first-child .ad-slider-piecewise-dot, +.l-ad-slider__wrap .ad-slider-piecewise-item:last-child .ad-slider-piecewise-dot { visibility: hidden; } -.l-ad-slider--horizontal .ad-slider-piecewise-label, .l-ad-slider--horizontal-reverse .ad-slider-piecewise-label { - position: absolute; +.l-ad-slider--horizontal .ad-slider-piecewise-label, +.l-ad-slider--horizontal-reverse .ad-slider-piecewise-label { display: inline-block; + visibility: visible; + white-space: nowrap; + position: absolute; top: 100%; left: 50%; - white-space: nowrap; font-size: 12px; - color: #333; + color: #333333; transform: translate(-50%, 8px); - visibility: visible; } -.l-ad-slider--vertical .ad-slider-piecewise-label, .l-ad-slider--vertical-reverse .ad-slider-piecewise-label { - position: absolute; +.l-ad-slider--vertical .ad-slider-piecewise-label, +.l-ad-slider--vertical-reverse .ad-slider-piecewise-label { display: inline-block; + visibility: visible; + white-space: nowrap; + position: absolute; top: 50%; left: 100%; - white-space: nowrap; font-size: 12px; - color: #333; + color: #333333; transform: translate(8px, -50%); - visibility: visible; } diff --git a/public/stylesheets/automator.css b/public/stylesheets/automator.css index e0b76b880..3a5702c27 100644 --- a/public/stylesheets/automator.css +++ b/public/stylesheets/automator.css @@ -1,20 +1,66 @@ -._-automator-split-pane-fix { - width: 100%; +:root { + --color-automator-controls-background: #d3d3d3; + --color-automator-controls-active: #0ba00e; + --color-automator-controls-inactive: #eeeeee; + --color-automator-controls-border: #767676; + --color-automator-active-line-background: #d8caeb; + --color-automator-active-line-outline: #7a49ff; + --color-automator-event-background: #9afa9a; + --color-automator-event-outline: #094e09; + --color-automator-error-background: #ac0617; + --color-automator-error-outline: #440108; + --color-automator-docs-font: black; + --color-blockmator-light-text: #dddddd; + --color-blockmator-block-border: #cfcfcf; + --color-blockmator-block-background: #f5f5f5; + --color-blockmator-block-command: #401090; + --color-blockmator-block-required: #50aaaa; + --color-blockmator-block-optional: #684700; + --color-blockmator-editor-background: white; +} + +:root .s-base--dark { + --color-automator-controls-background: #262626; + --color-automator-controls-active: #007c00; + --color-automator-controls-inactive: #273757; + --color-automator-controls-border: #929292; + --color-automator-active-line-background: #2b065c; + --color-automator-active-line-outline: #4400ff; + --color-automator-event-background: #004b00; + --color-automator-docs-font: white; + --color-blockmator-light-text: white; + --color-blockmator-block-border: #353535; + --color-blockmator-block-background: #000115; + --color-blockmator-block-command: #a142ff; + --color-blockmator-block-required: #005050; + --color-blockmator-block-optional: #684700; + --color-blockmator-editor-background: black; } .c-automator-tab { + width: 80%; + min-width: 100rem; +} + +.c-automator-split-pane { + width: 100%; height: 57rem; - width: 100rem; + background-color: #bbbbbb; } -.c-automator-tab > div { - height: 50rem; - width: 100rem; +.s-base--dark .c-automator-split-pane { + width: 100%; + background-color: #474747; } -.c-automator-tab--full-screen { - height: auto; - width: auto; +.c-automator-tab--full-screen .c-automator-split-pane { + width: 100%; + height: 100%; + position: fixed; + inset: 0; + z-index: 4; + margin-top: 0; + padding-bottom: 0; } .l-automator-tab { @@ -28,14 +74,16 @@ flex-direction: row; justify-content: space-between; margin-top: 1rem; + -webkit-user-select: none; user-select: none; } .c-automator-points-list-col { position: relative; - border: 0.15rem solid var(--color-text); - padding: 1rem; text-align: left; + border: var(--var-border-width, 0.15rem) solid var(--color-text); + border-radius: var(--var-border-radius, 0.5rem); + padding: 1rem; } .l-automator-points-list-side-col { @@ -53,27 +101,28 @@ } .c-automator-points-list-cell { - position: relative; + overflow: hidden; width: 100%; height: 48%; - padding: 1rem; - border: 0.15rem solid var(--color-text); + position: relative; text-align: left; - overflow: hidden; + border: var(--var-border-width, 0.15rem) solid var(--color-text); + border-radius: var(--var-border-radius, 0.5rem); + padding: 1rem; } .c-automator-points-list-symbol { - position: absolute; display: flex; - justify-content: center; - align-items: center; + width: 100%; + height: 100%; + position: absolute; top: 0; left: 0; + justify-content: center; + align-items: center; font-size: 15rem; opacity: 0.2; text-shadow: 0 0 2rem; - width: 100%; - height: 100%; pointer-events: none; } @@ -101,21 +150,6 @@ opacity: 0.6; } -.l-automator-tab--full-screen { - position: fixed; - left: 0; - right: 0; - top: -6.5rem; - bottom: 0; - margin-top: 0; - z-index: 4; -} - -.l-automator-tab--full-screen > div { - height: calc(100% - 6.5rem); - width: 100%; -} - .l-automator-pane { display: flex; flex-direction: column; @@ -126,149 +160,132 @@ flex: 1 1 auto; } -.l-automator-pane__controls { - flex: 0 0 auto; +.s-base--metro .l-automator-pane__content::-webkit-scrollbar-thumb { + border-radius: 0; } .c-automator__controls { - background-color: #262626; + background-color: var(--color-automator-controls-background); + border-bottom: 0.3rem solid var(--color-automator-active-line-outline); } .l-automator__controls { + display: flex; + flex: none; + flex-direction: column; +} + +.l-automator-button-row { display: flex; flex-direction: row; + height: 3rem; align-items: center; - justify-content: flex-start; - /* for corner buttons */ - position: relative; } .l-automator__button { display: flex; - align-items: center; justify-content: center; - padding: 0.3rem 0.8rem 0.3rem 0.8rem; + align-items: center; + padding: 0.3rem 0.8rem; } .l-automator__button--corner { position: absolute; + top: 0; right: 0; - top: 0 -} - -.c-automator__button { - margin: 0.4rem; - border-radius: 0.3rem; -} - -.c-automator__button-play--active { - border-color: #45a047; - color: #45a047; -} - -.c-automator__button--active { - border-color: #45a047; - color: #45a047; -} - -.l-automator__script-names { - flex-grow: 1; - display: flex; - flex-direction: row; - justify-content: space-evenly; - align-items: center; -} - -.l-automator__scripts-dropdown { - width: 90%; - height: 90%; - padding: 0.1rem; -} - -.l-automator__rename-input { - width: 90%; - height: 90%; - padding: 0.1rem; } .c-automator-editor__active-line { - background: rgb(43, 6, 92); - outline: 0.1rem solid rgb(68, 0, 255); + background: var(--color-automator-active-line-background); + outline: 0.1rem solid var(--color-automator-active-line-outline); } .c-automator-editor__active-line-gutter { font-weight: bold; - background: rgb(43, 6, 92); + background: var(--color-automator-active-line-background); + outline: 0.1rem solid var(--color-automator-active-line-outline); filter: brightness(200%); - outline: 0.1rem solid rgb(68, 0, 255); } .c-automator-editor__event-line { - background: rgb(0, 127, 0); - outline: 0.1rem solid rgb(25, 200, 25); + background: var(--color-automator-event-background); + outline: 0.1rem solid var(--color-automator-event-outline); } .c-automator-editor__event-line-gutter { font-weight: bold; - background: rgb(0, 127, 0); + background: var(--color-automator-event-background); + outline: 0.1rem solid var(--color-automator-event-outline); filter: brightness(200%); - outline: 0.1rem solid rgb(25, 200, 25); } .c-automator-editor__error-line { - background: rgb(172, 6, 23); - outline: 0.1rem solid rgb(255, 0, 64); + background: var(--color-automator-error-background); + outline: 0.1rem solid var(--color-automator-error-outline); } .c-automator-editor__error-line-gutter { font-weight: bold; - background: rgb(172, 6, 23); + background: var(--color-automator-error-background); + outline: 0.1rem solid var(--color-automator-error-outline); filter: brightness(200%); - outline: 0.1rem solid rgb(255, 0, 64); } .c-automator-editor { + text-align: left; font-family: Typewriter, serif; font-size: 1.4rem; - text-align: left; - border-bottom: 0.15rem solid #262626; + border-bottom: 0.15rem solid var(--color-automator-controls-background); } .l-automator-editor { display: flex; - flex-direction: column; flex: 1 1 auto; + flex-direction: column; } .l-automator-editor__codemirror-container { display: flex; - flex-direction: column; flex: 1 1 auto; + flex-direction: column; + z-index: 1; } .CodeMirror-hint { - font-size: 1rem; font-family: Typewriter, serif; + font-size: 1rem; +} + +.s-base--metro .CodeMirror-vscrollbar::-webkit-scrollbar-thumb { + border-radius: 0; } .cm-s-liquibyte.CodeMirror, .cm-s-panda-syntax.CodeMirror { - font-size: 1.4rem; - font-family: Typewriter, serif; - line-height: 1.6rem; + /* required for expanding into pane */ flex: 1 1 auto; + font-family: Typewriter, serif; + font-size: 1.4rem; + line-height: 1.6rem; } .c-automator-docs { - border-right: 0.15rem solid #262626; - border-bottom: 0.15rem solid #262626; - background-color: lightgray; - padding-left: 1rem; - font-size: 1.4rem; - color: black; - text-align: left; overflow: auto; + position: relative; + z-index: 1; + text-align: left; + font-size: 1.4rem; + color: var(--color-automator-docs-font); + background-color: white; + border-right: 0.15rem solid var(--color-automator-controls-background); + border-bottom: 0.15rem solid var(--color-automator-controls-background); + padding: 1rem; +} + +.s-base--dark .c-automator-docs { + color: var(--color-automator-docs-font); + background-color: black; } .c-automator-docs-page { @@ -286,21 +303,61 @@ margin-left: 2rem; } +.c-automator-docs--button { + width: 2.8rem; + height: 2.2rem; + border-width: 0.1rem; + border-radius: var(--var-border-radius, 0.3rem); + margin: 0.1rem; + cursor: pointer; +} + +.c-automator-docs-script-select { + width: 100%; + height: calc(2.1rem + 1rem / 3 - var(--var-border-width, 0rem) * 2); + text-align: left; + font-family: Typewriter, serif; + font-size: 1.2rem; + color: var(--color-automator-docs-font); + background-color: var(--color-automator-controls-inactive); + border: var(--var-border-width, 0.2rem) solid var(--color-automator-controls-border); + border-radius: var(--var-border-radius, 0.3rem); + padding: 0.1rem 0.5rem 0; + cursor: pointer; +} .o-automator-error-underline { display: inline-block; position: relative; } -.o-automator-error-underline:before { +.o-automator-error-underline::before { content: "~~~~~~~~~~~~"; - font-size: 0.6em; - font-weight: 700; - font-family: Times New Roman, Serif; - color: red; + overflow: hidden; width: 100%; position: absolute; top: 12px; left: -1px; - overflow: hidden; -} \ No newline at end of file + color: red; +} + +.c-automator-input-required { + background: var(--color-blockmator-block-required); +} + +.c-automator-input-optional { + color: var(--color-blockmator-light-text); + background: var(--color-blockmator-block-optional); +} + +.c-automator-block-row-active { + background: var(--color-automator-active-line-background); +} + +.c-automator-block-row-event { + background: var(--color-automator-event-background); +} + +.c-automator-block-row-error { + background: var(--color-automator-error-background); +} diff --git a/public/stylesheets/codemirror/lint.css b/public/stylesheets/codemirror/lint.css index f097cfe34..5911477d5 100644 --- a/public/stylesheets/codemirror/lint.css +++ b/public/stylesheets/codemirror/lint.css @@ -5,8 +5,12 @@ .CodeMirror-lint-tooltip { background-color: #ffd; - border: 1px solid black; - border-radius: 4px 4px 4px 4px; + border: var(--var-border-width, 1px) solid black; + border-radius: + var(--var-border-radius, 4px) + var(--var-border-radius, 4px) + var(--var-border-radius, 4px) + var(--var-border-radius, 4px); color: black; font-family: monospace; font-size: 10pt; diff --git a/public/stylesheets/codemirror/liquibyte.css b/public/stylesheets/codemirror/liquibyte.css index da8b2e82b..1270e8ddc 100644 --- a/public/stylesheets/codemirror/liquibyte.css +++ b/public/stylesheets/codemirror/liquibyte.css @@ -1,9 +1,13 @@ .cm-s-liquibyte.CodeMirror { - background-color: #000; - color: #fff; + background-color: #fff; + color: #000; line-height: 1.2em; font-size: 1em; } +.s-base--dark .cm-s-liquibyte.CodeMirror { + background-color: #000; + color: #fff; +} .cm-s-liquibyte .CodeMirror-focused .cm-matchhighlight { text-decoration: underline; text-decoration-color: #0f0; @@ -19,36 +23,52 @@ text-decoration-color: #404040; text-decoration-style: dotted; } -.cm-s-liquibyte .CodeMirror-gutters { background-color: #262626; border-right: 1px solid #505050; padding-right: 0.8em; } +.cm-s-liquibyte .CodeMirror-gutters { background-color: var(--color-automator-controls-background); border-right: 1px solid #999; padding-right: 0.8em; } .cm-s-liquibyte .CodeMirror-gutter-elt div { font-size: 1.2em; } .cm-s-liquibyte .CodeMirror-guttermarker { } .cm-s-liquibyte .CodeMirror-guttermarker-subtle { } -.cm-s-liquibyte .CodeMirror-linenumber { color: #606060; padding-left: 0; } -.cm-s-liquibyte .CodeMirror-cursor { border-left: 1px solid #eee; } +.cm-s-liquibyte .CodeMirror-linenumber { color: #000000; padding-left: 0; } +.cm-s-liquibyte .CodeMirror-cursor { border-left: 1px solid #262626; } + +.s-base--dark .cm-s-liquibyte .CodeMirror-cursor { border-left: 1px solid #eee; } +.s-base--dark .cm-s-liquibyte .CodeMirror-linenumber { color: #ffffff; padding-left: 0; } + +.s-base--dark .cm-s-liquibyte .CodeMirror-gutters { border-right: 1px solid #505050; } .cm-s-liquibyte span.cm-comment { color: #008000; } .cm-s-liquibyte span.cm-def { color: #ffaf40; font-weight: bold; } -.cm-s-liquibyte span.cm-keyword { color: #c080ff; font-weight: bold; } +.cm-s-liquibyte span.cm-keyword { color: #9d68d3; font-weight: bold; } .cm-s-liquibyte span.cm-builtin { color: #ffaf40; font-weight: bold; } .cm-s-liquibyte span.cm-variable { color: #5967ff; font-weight: bold; } .cm-s-liquibyte span.cm-string { color: #ff8000; } -.cm-s-liquibyte span.cm-number { color: #0f0; font-weight: bold; } +.cm-s-liquibyte span.cm-number { color: rgb(0, 190, 0); font-weight: bold; } .cm-s-liquibyte span.cm-atom { color: #bf3030; font-weight: bold; } -.cm-s-liquibyte span.cm-variable-2 { color: #21a8a8; font-weight: bold; } +.s-base--dark .cm-s-liquibyte span.cm-keyword { color: #c080ff; } +.s-base--dark .cm-s-liquibyte span.cm-number { color: #0f0; } + +.cm-s-liquibyte span.cm-variable-2 { color: #6ca7a7; font-weight: bold; } .cm-s-liquibyte span.cm-variable-3, .cm-s-liquibyte span.cm-type { color: #c080ff; font-weight: bold; } .cm-s-liquibyte span.cm-property { color: #999; font-weight: bold; } -.cm-s-liquibyte span.cm-operator { color: #fff; } +.cm-s-liquibyte span.cm-operator { color: #000; } + +.s-base--dark .cm-s-liquibyte span.cm-variable-2 { color: #21a8a8; } +.s-base--dark .cm-s-liquibyte span.cm-operator { color: #fff; } +.s-base--dark .cm-s-liquibyte span.cm-qualifier { color: #fff700; font-weight: bold; } .cm-s-liquibyte span.cm-meta { color: #0f0; } -.cm-s-liquibyte span.cm-qualifier { color: #fff700; font-weight: bold; } +.cm-s-liquibyte span.cm-qualifier { color: #bdb700; font-weight: bold; } .cm-s-liquibyte span.cm-bracket { color: #cc7; } .cm-s-liquibyte span.cm-tag { color: #ff0; font-weight: bold; } .cm-s-liquibyte span.cm-attribute { color: #c080ff; font-weight: bold; } .cm-s-liquibyte span.cm-error { color: #f00; } -.cm-s-liquibyte span.cm-blob { color: #fc2; } +.cm-s-liquibyte span.cm-blob { color: rgb(228, 181, 26); } -.cm-s-liquibyte div.CodeMirror-selected { background-color: rgba(255, 0, 0, 0.25); } +.s-base--dark .cm-s-liquibyte span.cm-blob { color: #fc2; } + +.cm-s-liquibyte div.CodeMirror-selected { background-color: rgba(255, 0, 0, 0.1); } + +.s-base--dark .cm-s-liquibyte div.CodeMirror-selected { background-color: rgba(255, 0, 0, 0.25); } .cm-s-liquibyte span.cm-compilation { background-color: rgba(255, 255, 255, 0.12); } diff --git a/public/stylesheets/codemirror/show-hint.css b/public/stylesheets/codemirror/show-hint.css index 5617ccca2..c01161f9a 100644 --- a/public/stylesheets/codemirror/show-hint.css +++ b/public/stylesheets/codemirror/show-hint.css @@ -10,8 +10,8 @@ -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); box-shadow: 2px 3px 5px rgba(0,0,0,.2); - border-radius: 3px; - border: 1px solid silver; + border-radius: var(--var-border-radius, 3px); + border: var(--var-border-width, 1px) solid silver; background: white; font-size: 90%; @@ -24,7 +24,7 @@ .CodeMirror-hint { margin: 0; padding: 0 4px; - border-radius: 2px; + border-radius: var(--var-border-radius, 2px); white-space: pre; color: black; cursor: pointer; diff --git a/public/stylesheets/glyphs.css b/public/stylesheets/glyphs.css index 68451bc9a..ba7917f8d 100644 --- a/public/stylesheets/glyphs.css +++ b/public/stylesheets/glyphs.css @@ -1,14 +1,40 @@ +:root { + --color-background: white; + --color-glyph-sac-text-input: #f0f0f0; + --color-glyph-sac-text-input--focused: #d6d6d6; + --color-glyph-equipping: #444444; + --color-background-dark-green-gray: #dbe6db; + --color-text-light-blue-gray: #3b3b46; + --color-glyph-selection: #a1a112; + --color-glyph-empty-slot: #dcdcdc; + --color-glyph-scrollbar: #94cc97; + --color-glyph-undo-disabled: #a0a0a0; +} + +:root .s-base--dark { + --color-background: black; + --color-glyph-sac-text-input: #333333; + --color-glyph-sac-text-input--focused: #555555; + --color-glyph-equipping: #adadad; + --color-background-dark-green-gray: #113311; + --color-text-light-blue-gray: #d4d4ff; + --color-glyph-selection: #b4b420; + --color-glyph-empty-slot: #333333; + --color-glyph-scrollbar: #08450b; + --color-glyph-undo-disabled: #656565; +} + .l-glyph-component { display: inline-flex; - align-items: center; - justify-content: center; - box-sizing: border-box; position: relative; + justify-content: center; + align-items: center; + box-sizing: border-box; } .c-glyph-component { font-family: Typewriter; - background: black; + background: var(--color-background); } .l-new-glyph { @@ -18,7 +44,19 @@ z-index: 5; color: black; background-color: #ffff00; - border-radius: 0.2rem; + border-radius: var(--var-border-radius, 0.2rem); + padding: 0.2rem; +} + +.l-glyph-info { + position: absolute; + font-size: 0.9rem; + bottom: -0.5rem; + right: -0.5rem; + z-index: 5; + color: white; + background-color: gray; + border-radius: var(--var-border-radius, 0.2rem); padding: 0.2rem; } @@ -37,27 +75,34 @@ .l-glyph-sacrifice-options { display: inline-flex; flex-direction: column; - padding: 1rem; + width: 27rem; + position: relative; margin-top: -0.2rem; margin-left: 0.5rem; - position: relative; - width: 27rem; + padding: 1rem; } .c-glyph-sacrifice-options { - color: var(--color-reality-light); - background: black; - border: 0.2rem solid var(--color-reality); - border-radius: 0.5rem; + position: relative; + color: var(--color-reality-dark); + background: var(--color-background); + border: var(--var-border-width, 0.2rem) solid var(--color-reality-dark); + border-radius: var(--var-border-radius, 0.5rem); border-top-left-radius: 0; border-top-right-radius: 0; padding: 0.8rem 0.8rem 0.5rem; - position: relative; } -.s-base--metro .c-glyph-sacrifice-options { - border-width: 0.1rem; - border-radius: 0; +.s-base--dark .c-glyph-sacrifice-options { + color: var(--color-reality-light); +} + +.c-glyph-inventory-management-with-border { + border-radius: var(--var-border-radius, 0.5rem); +} + +.c-glyph-sacrifice-options-container { + border-radius: var(--var-border-radius, 0.5rem); } .l-glyph-sacrifice-options__header { @@ -66,14 +111,18 @@ .l-glyph-sacrifice-options__help { position: absolute; - left: calc(100% - 1.8rem); top: 0; + left: calc(100% - 1.8rem); z-index: 2; } .c-glyph-sacrifice-options__help { - color: var(--color-reality-light); font-size: 1.2rem; + color: var(--color-reality-dark); +} + +.s-base--dark .c-glyph-sacrifice-options__help { + color: var(--color-reality-light); } .l-glyph-sacrifice-options__option { @@ -82,98 +131,108 @@ .c-glyph-sacrifice-options__option { display: flex; - justify-content: center; - align-items: center; - position: relative; - cursor: pointer; - border: 0.2rem solid; - transition-duration: 0.2s; - border-radius: 0.5rem; width: 4.6rem; height: 3rem; -} - -.s-base--metro .c-glyph-sacrifice-options__option { - border-width: 0.1rem; - border-radius: 0; + position: relative; + justify-content: center; + align-items: center; + border: var(--var-border-width, 0.2rem) solid; + border-radius: var(--var-border-radius, 0.5rem); + transition-duration: 0.2s; + cursor: pointer; } .c-glyph-sacrifice-options__option:hover { + color: var(--color-reality-dark); + background-color: var(--color-reality-light); + border-color: var(--color-reality-dark); +} + +.s-base--dark .c-glyph-sacrifice-options__option:hover { color: var(--color-reality-light); + background-color: var(--color-background); border-color: var(--color-reality-light); } .c-glyph-sacrifice-options__option--active { - color: black; - border-color: var(--color-reality-light); - background-color: var(--color-reality-light); + color: var(--color-text-inverted); + background-color: var(--color-reality-dark); + border-color: var(--color-reality-dark); pointer-events: none; } +.s-base--dark .c-glyph-sacrifice-options__option--active { + background-color: var(--color-reality-light); + border-color: var(--color-reality-light); +} + .c-glyph-sacrifice-options__option--inactive { - color: var(--color-reality); - border-color: var(--color-reality); + color: var(--color-reality-dark); + border-color: var(--color-reality-dark); } .c-glyph-sacrifice-options__option__tooltip { display: flex; - justify-content: center; - align-items: center; - opacity: 0; - transition-duration: 0.2s; - font-size: 1.4rem; - font-weight: bold; - border: 0.2rem solid var(--color-reality); - border-radius: 0.5rem; - font-family: Typewriter; - color: var(--color-reality); - background: black; width: 17rem; - line-height: 2rem; - padding: 0.3rem 0; - z-index: 2; - pointer-events: none; position: absolute; top: 100%; + z-index: 2; + justify-content: center; + align-items: center; + font-family: Typewriter; + font-size: 1.4rem; + font-weight: bold; + line-height: 2rem; + opacity: 0; + color: var(--color-reality); + background: var(--color-background); + border: var(--var-border-width, 0.2rem) solid var(--color-reality); + border-radius: var(--var-border-radius, 0.5rem); + padding: 0.3rem 0; + transition-duration: 0.2s; + pointer-events: none; } -.s-base--metro .c-glyph-sacrifice-options__option__tooltip { - border-width: 0.1rem; - border-radius: 0; -} - -.c-glyph-sacrifice-options__option__tooltip:after { +.c-glyph-sacrifice-options__option__tooltip::after { + content: " "; + width: 0; position: absolute; top: 0; left: 50%; - margin-left: -0.7rem; - margin-bottom: 0; - width: 0; - border-bottom: 0 solid var(--color-reality-light); - border-right: 0.7rem solid transparent; - border-left: 0.7rem solid transparent; - content: " "; - transition-duration: 0.2s; z-index: 0; + border-right: 0.7rem solid transparent; + border-bottom: 0 solid var(--color-reality); + border-left: 0.7rem solid transparent; + margin-left: -0.7rem; + transition-duration: 0.2s; +} + +.s-base--dark .c-glyph-sacrifice-options__option__tooltip::after { + border-bottom: 0 solid var(--color-reality-light); } .c-glyph-sacrifice-options__option:hover .c-glyph-sacrifice-options__option__tooltip { - opacity: 1; top: calc(100% + 0.9rem); - border-color: var(--color-reality-light); + opacity: 1; + color: var(--color-reality); + border-color: var(--color-reality); +} + +.s-base--dark .c-glyph-sacrifice-options__option:hover .c-glyph-sacrifice-options__option__tooltip { color: var(--color-reality-light); + border-color: var(--color-reality-light); } .s-base--metro .c-glyph-sacrifice-options__option:hover .c-glyph-sacrifice-options__option__tooltip { top: calc(100% + 0.8rem); } -.c-glyph-sacrifice-options__option:hover .c-glyph-sacrifice-options__option__tooltip:after { +.c-glyph-sacrifice-options__option:hover .c-glyph-sacrifice-options__option__tooltip::after { border-bottom-width: 0.7rem; margin-top: -0.9rem; } -.s-base--metro .c-glyph-sacrifice-options__option:hover .c-glyph-sacrifice-options__option__tooltip:after { +.s-base--metro .c-glyph-sacrifice-options__option:hover .c-glyph-sacrifice-options__option__tooltip::after { margin-top: -0.8rem; } @@ -184,35 +243,59 @@ .l-glyph-sacrifice-options__rarity-slider-div { display: flex; flex-direction: row; - align-items: center; justify-content: space-between; + align-items: center; padding-top: 0.3rem; padding-bottom: 0.3rem; } .c-glyph-sacrifice-options__rarity-slider-process { + background-color: var(--color-reality); +} + +.s-base--dark .c-glyph-sacrifice-options__rarity-slider-process { background-color: var(--color-reality-light); } .c-glyph-sacrifice-options__rarity-slider-bg { + background-color: var(--color-reality-dark); +} + +.s-base--dark .c-glyph-sacrifice-options__rarity-slider-bg { background-color: var(--color-reality); } .c-glyph-sacrifice-options__rarity-slider-handle { + color: var(--color-reality-dark); + background-color: var(--color-background) !important; + border-radius: var(--var-border-radius, 0.8rem) !important; + box-shadow: 0 0 0 0.1rem var(--color-reality-dark) !important; +} + +.s-base--dark .c-glyph-sacrifice-options__rarity-slider-handle { color: var(--color-reality-light); - background-color: black !important; + background-color: var(--color-background) !important; + border-radius: var(--var-border-radius, 0.8rem) !important; box-shadow: 0 0 0 0.1rem var(--color-reality-light) !important; - border-radius: 0.8rem !important; +} + +.c-glyph-sacrifice-options__rarity-slider-handle:hover { + color: black; + background-color: var(--color-reality-light) !important; + border-color: var(--color-reality-light); } .c-glyph-sacrifice-options__advanced { + color: var(--color-reality-dark); +} + +.s-base--dark .c-glyph-sacrifice-options__advanced { color: var(--color-reality-light); } .l-glyph-inventory-management { display: flex; - flex-direction: row; - flex-wrap: wrap; + flex-flow: row wrap; justify-content: center; } @@ -222,16 +305,20 @@ } .c-auto-sac-type-tab__input { - width: 2.5em; - height: 1.5em; - font-size: 1.2rem; - background-color: #333; - color: var(--color-reality-light); - border-radius: 0.2rem; - border: solid 0.1rem; + width: 3.5rem; + height: 1.7rem; text-align: center; + font-size: 1.2rem; + color: var(--color-reality-dark); -webkit-appearance: none; -moz-appearance: textfield; + background-color: var(--color-glyph-sac-text-input); + border: solid 0.1rem; + border-radius: var(--var-border-radius, 0.2rem); +} + +.s-base--dark .c-auto-sac-type-tab__input { + color: var(--color-reality-light); } .c-auto-sac-effect-tab__input::-webkit-inner-spin-button, @@ -242,29 +329,37 @@ } .c-auto-sac-effect-tab__input { - width: 1.5em; - height: 1.5em; - font-size: 1.2rem; + width: 1.5rem; + height: 1.5rem; text-align: center; - background-color: #333; - color: var(--color-reality-light); - border-radius: 0.2rem; - border: solid 0.1rem; + font-size: 1.2rem; + color: var(--color-reality-dark); -webkit-appearance: none; -moz-appearance: textfield; + background-color: var(--color-glyph-sac-text-input); + border: solid 0.1rem; + border-radius: var(--var-border-radius, 0.2rem); margin: 0; } +.s-base--dark .c-auto-sac-effect-tab__input { + color: var(--color-reality-light); +} + .c-auto-sac-effect-tab__checkbox { - width: 1.5em; - height: 1.5em; - border-radius: 0.2rem; + width: 1.5rem; + height: 1.5rem; border: solid 0.1rem; + border-radius: var(--var-border-radius, 0.2rem); } .c-auto-sac-type-tab__input:focus { font-weight: bold; - background-color: #555; + background-color: var(--color-glyph-sac-text-input--focused); + box-shadow: 0 0 0.4rem 0.1rem var(--color-reality-dark); +} + +.s-base--dark .c-auto-sac-type-tab__input:focus { box-shadow: 0 0 0.4rem 0.1rem var(--color-reality-light); } @@ -281,11 +376,11 @@ } .c-glyph-sacrifice-options__advanced-toggle { - background: black; - border-radius: 0.2rem; - border: solid 0.1rem; + font-family: Typewriter, serif; font-size: 1.2rem; - font-family: Typewriter, serif + background: black; + border: solid 0.1rem; + border-radius: var(--var-border-radius, 0.2rem); } .l-advanced-sac-options-for-glyph-type { @@ -302,30 +397,35 @@ .l-auto-sac-type-tab__row-wrapper { display: flex; flex-direction: row; - align-items: center; justify-content: space-between; + align-items: center; margin: 0.25rem 0.5rem; } .c-auto-sac-type-tab__effect-desc { - border-radius: 0.5em; - border: 0.1rem solid; - padding: 0.25em 1.5em; - min-height: 3em; + min-height: 3rem; position: relative; + border: 0.1rem solid; + border-radius: var(--var-border-radius, 0.5rem); + padding: 0.25rem 1.5rem; + filter: brightness(60%); +} + +.s-base--dark .c-auto-sac-type-tab__effect-desc { + filter: none; } .c-auto-sac-type-tab__effect-desc--active { - cursor: pointer; - opacity: 1; font-weight: bold; + opacity: 1; text-shadow: 0 0 0.3rem; + cursor: pointer; } .c-auto-sac-type-tab__effect-desc--inactive { - cursor: pointer; opacity: 0.7; transition: opacity 0.2s; + cursor: pointer; } .c-auto-sac-type-tab__effect-desc--inactive:hover { @@ -336,105 +436,116 @@ .l-specified-effect-tab__effect-desc { display: inline-flex; width: 100%; - align-items: center; justify-content: center; + align-items: center; } .l-auto-sac-type-tab__effect-desc { display: inline-flex; width: calc(100% - 5rem); - align-items: center; justify-content: center; + align-items: center; } .l-glyph-sacrifice-options__advanced-type-select { - font-size: 2em; - width: 3em; - height: 3em; display: inline-flex; - align-items: center; + width: 3rem; + height: 3rem; justify-content: center; - border-radius: 50%; + align-items: center; + font-size: 2rem; + border-radius: var(--var-border-radius, 50%); margin-top: 0.4rem; } .c-glyph-sacrifice-options__advanced-type-select { - color: #888; - font-size: 2rem; width: 2.5rem; height: 2.5rem; - margin: 1rem 0.25rem 0.25rem; font-family: Typewriter; - cursor: pointer; + font-size: 2rem; line-height: 0.1rem; + color: #888888; + margin: 1rem 0.25rem 0.25rem; transition-duration: 0.15s; + cursor: pointer; } .c-glyph-sacrifice-options__advanced-type-select:hover { - background: #131; - box-shadow: 0 0 0.8rem 0.4rem #131; -} - -.l-glyph-auto-pick-options { - display: inline-flex; - flex-direction: column; - padding: 2rem; - margin-top: 2rem; + background: var(--color-background-dark-green-gray); + box-shadow: 0 0 0.8rem 0.4rem var(--color-background-dark-green-gray); } .c-glyph-auto-pick-options { background: black; } -.l-glyph-auto-pick-options__option { - box-sizing: border-box; +.l-glyph-auto-pick-options__container { + display: flex; + flex-direction: row; + justify-content: center; } .c-glyph-auto-pick-options__option { - cursor: pointer; + display: flex; + flex-direction: column; + width: 12rem; + height: 5rem; + justify-content: center; + font-size: 1rem; font-weight: bold; - padding: 0.5rem; - border-width: 0.1rem 0.2rem; - border-style: solid; - margin-bottom: -0.1rem; - transition: color 0.2s; - position: relative; -} - -.s-base--metro .c-glyph-auto-pick-options__option { - border-width: 0.1rem; + border: var(--var-border-width, 0.2rem) solid; + border-radius: var(--var-border-radius, 0.5rem); + margin: 0 0.5rem 1rem; + padding: 0 0.5rem; + transition-duration: 0.2s; + -webkit-user-select: none; + user-select: none; + cursor: pointer; } .c-glyph-auto-pick-options__option:hover { + color: var(--color-reality-dark); + background-color: var(--color-reality-light); + border-color: var(--color-reality-dark); +} + +.s-base--dark .c-glyph-auto-pick-options__option:hover { color: var(--color-reality-light); + background-color: var(--color-background); + border-color: var(--color-reality-light); } .c-glyph-auto-pick-options__option--active { - color: black; - background-color: var(--color-reality); - border-color: var(--color-reality); - z-index: 1; + color: var(--color-text-inverted); + background-color: var(--color-reality-dark); + border-color: var(--color-reality-dark); pointer-events: none; } +.s-base--dark .c-glyph-auto-pick-options__option--active { + color: var(--color-text-inverted); + background-color: var(--color-reality-light); + border-color: var(--color-reality-light); +} + .c-glyph-auto-pick-options__option--inactive { - color: var(--color-reality); - border-color: var(--color-reality); + color: var(--color-reality-dark); + border-color: var(--color-reality-dark); } .c-glyph-tooltip__effect { font-weight: normal; text-shadow: none; - margin-bottom: 1em; + margin-bottom: 1rem; } .l-glyph-tooltip { display: flex; flex-direction: column; - align-items: center; width: 27.4rem; - padding: 0.5rem 0; position: absolute; + align-items: center; + padding: 0.5rem 0; } .l-glyph-tooltip--up-left { @@ -454,22 +565,15 @@ } .c-glyph-tooltip { - background-color: black; - color: #fff; + text-align: center; font-size: 1.2rem; font-weight: normal; - text-align: center; - border-radius: 0.6rem; - transition: opacity 0.3s; text-shadow: none; - padding: 0.6rem; + border: var(--var-border-width, 0.2rem) solid; + border-radius: var(--var-border-radius, 0.6rem); + padding: 0 0.6rem; padding-bottom: 0.6rem; - border: 0.2rem solid black; -} - -.s-base--metro .c-glyph-tooltip { - border-radius: 0; - border-width: 0.1rem; + transition: opacity 0.3s; } .c-glyph-tooltip__description { @@ -484,35 +588,28 @@ display: flex; flex-direction: column; width: calc(100% + 2.4rem); - border: 0.2rem solid; - padding: 0.3rem 1rem 0.7rem; font-weight: bold; - margin-top: -4rem; - background-color: black; - border-radius: 0.8rem; -} - -.s-base--metro .c-glyph-tooltip__header { - border-radius: 0; - border-width: 0.1rem; + border: var(--var-border-width, 0.2rem) solid; + border-radius: var(--var-border-radius, 0.8rem); + margin: -0.6rem; + padding: 0.3rem 1rem 0.7rem; } .l-glyph-tooltip__effects { - margin-top: 0.8rem; + margin-top: 1rem; margin-bottom: -0.8rem; } .c-glyph-tooltip__sacrifice { - color: #b4b4b4; font-size: 1rem; font-weight: normal; } .c-glyph-tooltip__sacrifice--touchable { - color: #d4d4ff; - border: 0.1rem solid #d4d4ff; - border-radius: 0.5em; - padding: 0.25em 1em; + color: var(--color-text-light-blue-gray); + border: 0.1rem solid var(--color-text-light-blue-gray); + border-radius: var(--var-border-radius, 0.5rem); + padding: 0.25rem 1rem; } .l-modal-glyph-selection__row { @@ -522,55 +619,58 @@ .l-modal-glyph-selection__glyph { margin: 1rem; + cursor: pointer; } .l-modal-glyph-selection__glyph--selected { - box-shadow: #B4B420 0 0 1rem 0.5rem !important; + box-shadow: var(--color-glyph-selection) 0 0 1rem 0.5rem !important; + cursor: auto; } .l-equipped-glyphs { display: flex; flex-direction: column; + width: 20rem; align-content: center; align-items: center; padding: 1rem; - width: 20rem; } .l-equipped-glyphs__slots { width: 18rem; height: 18rem; position: relative; - background-color: rgba(0, 0, 0, 0); + background-color: rgba(0, 0, 0, 0%); + margin-bottom: 1rem; } .l-equipped-glyphs__dropzone { - border-radius: 50%; width: 7rem; height: 7rem; position: absolute; - left: 50%; top: 50%; + left: 50%; + background-color: rgba(0, 0, 0, 0%); + border-radius: 50%; transform: translate(-50%, -50%); - background-color: rgba(0, 0, 0, 0); } .l-equipped-glyphs__empty { - border-radius: 50%; width: 5rem; height: 5rem; + border-radius: 50%; } .c-equipped-glyphs__empty { - background-color: gainsboro; + background-color: var(--color-glyph-empty-slot); } .t-dark-metro .c-equipped-glyphs__empty { - background-color: #333; + background-color: #333333; } .t-dark .c-equipped-glyphs__empty { - background-color: #333; + background-color: #333333; } .t-s6 .c-equipped-glyphs__empty, @@ -579,7 +679,7 @@ } .c-equipped-glyphs__empty--dragover { - box-shadow: 0 0 0.5rem 0.25rem #444; + box-shadow: 0 0 0.5rem 0.25rem var(--color-glyph-equipping); } .l-equipped-glyphs__buttons { @@ -589,12 +689,6 @@ align-items: center; } -.l-glyph-equip-button { - width: 100%; - height: 4rem; - margin: 0.5rem; -} - .l-glyph-set-save__header { margin-top: 1rem; } @@ -602,9 +696,9 @@ .c-glyph-set-save-container { display: flex; flex-wrap: wrap; + max-width: 30rem; justify-content: center; margin: 1rem auto 0; - max-width: 30rem; } .c-glyph-single-set-save { @@ -628,43 +722,48 @@ } .c-glyph-sets-save-name__input { - width: 16rem; - height: 1.5em; - font-size: 1.35rem; - background-color: black; - color: var(--color-reality-light); - border-radius: 0.2rem; - border: none; - border-bottom: 0.1rem solid; + width: 17rem; + height: 1.5rem; text-align: center; - margin-bottom: 0.5rem; + font-family: Typewriter, serif; + font-size: 1.35rem; + color: var(--color-reality-dark); -webkit-appearance: none; -moz-appearance: textfield; + background-color: var(--color-background); + border: none; + border-bottom: 0.1rem solid; + border-radius: var(--var-border-radius, 0.2rem); + margin-bottom: 0.5rem; +} + +.s-base--dark .c-glyph-sets-save-name__input { + color: var(--color-reality-light); } .c-glyph-set-save-setting-button { width: 12.5rem; height: 2.5rem; - font-size: 0.9rem; font-family: Typewriter, serif; + font-size: 0.9rem; font-weight: bold; - background-color: black; - color: var(--color-reality-light); - border: 0.2rem solid var(--color-reality); - border-radius: 0.5rem; + color: var(--color-reality-dark); + background-color: var(--color-background); + border: var(--var-border-width, 0.2rem) solid var(--color-reality-dark); + border-radius: var(--var-border-radius, 0.5rem); margin: 0 0.4rem 1rem; transition-duration: 0.2s; cursor: pointer; } -.s-base--metro .c-glyph-set-save-setting-button { - border-width: 0.1rem; - border-radius: 0; +.s-base--dark .c-glyph-set-save-setting-button { + color: var(--color-reality-light); + border: var(--var-border-width, 0.2rem) solid var(--color-reality); } .c-glyph-set-save-setting-button:hover { - background-color: var(--color-reality-light); color: black; + background-color: var(--color-reality-light); } .c-glyph-set-save-setting-button--disabled { @@ -672,32 +771,40 @@ } .c-glyph-set-save-setting-button--disabled:hover { - background-color: black; + color: var(--color-reality-dark); + background-color: var(--color-background); +} + +.s-base--dark .c-glyph-set-save-setting-button--disabled:hover { color: var(--color-reality-light); + background-color: var(--color-background); } .c-glyph-set-save-button { height: 2.5rem; - color: var(--color-reality-light); - background-color: black; - border: 0.2rem solid var(--color-reality); - border-radius: 0.5rem; - cursor: pointer; - transition-duration: 0.2s; text-align: center; + font-family: Typewriter, serif; font-size: 1rem; font-weight: bold; - font-family: Typewriter, serif; + color: var(--color-reality-dark); + background-color: var(--color-background); + border: var(--var-border-width, 0.2rem) solid var(--color-reality-dark); + border-radius: var(--var-border-radius, 0.5rem); + transition-duration: 0.2s; + cursor: pointer; +} + +.s-base--dark .c-glyph-set-save-button { + color: var(--color-reality-light); } .c-glyph-set-save-button:hover { - color: black; - background-color: var(--color-reality-light); + color: var(--color-background); + background-color: var(--color-reality-dark); } -.s-base--metro .c-glyph-set-save-button { - border-width: 0.1rem; - border-radius: 0; +.s-base--dark .c-glyph-set-save-button:hover { + background-color: var(--color-reality-light); } .c-glyph-set-save-button--unavailable { @@ -706,15 +813,26 @@ cursor: default; } +.s-base--dark .c-glyph-set-save-button--unavailable { + color: var(--color-bad); + border-color: var(--color-bad); + cursor: default; +} + .c-glyph-set-save-button--unavailable:hover { color: var(--color-bad); - background: black; + background: var(--color-background); +} + +.s-base--dark .c-glyph-set-save-button--unavailable:hover { + color: var(--color-bad); + background: var(--color-background); } .l-glyph-set-save-spacing { - height: 6rem; display: flex; flex-direction: column; + height: 6rem; place-content: flex-end; } @@ -723,7 +841,7 @@ } .l-current-glyph-effects__capped-header { - margin-bottom: 1em; + margin-bottom: 1rem; } .l-current-glyph-effects { @@ -732,7 +850,7 @@ height: initial; /* Moz styling for scrolbar */ - scrollbar-color: #08450b #00000000; + scrollbar-color: var(--color-glyph-scrollbar) #00000000; scrollbar-width: thin; } @@ -741,28 +859,44 @@ } .l-current-glyph-effects::-webkit-scrollbar-thumb { - background: #08450b; + background: var(--color-glyph-scrollbar); +} + +.s-base--metro .l-current-glyph-effects::-webkit-scrollbar-thumb { + border-radius: 0; } .c-current-glyph-effects { - border: 0.1rem solid #b8b8b8; - border-radius: 0.5rem; + overflow: auto; + width: 45rem; + height: 31rem; + position: relative; + color: var(--color-reality-dark); + background: var(--color-background); + border: var(--var-border-width, 0.2rem) solid var(--color-reality-dark); + border-radius: var(--var-border-radius, 0.5rem); border-top-left-radius: 0; border-top-right-radius: 0; - padding: 1.2rem; margin-top: 0; margin-bottom: 0; - height: 30rem; - width: 45rem; - overflow: auto; + padding: 0.8rem 0.8rem 1.8rem; +} + +.s-base--dark .c-current-glyph-effects { + color: var(--color-reality-light); +} + +.c-current-glyph-effects-with-top-border { + border-radius: var(--var-border-radius, 0.5rem); } .pelle-current-glyph-effects { color: var(--color-pelle--base); } -.s-base--metro .c-current-glyph-effects { - border-radius: 0; +.c-glyph-tooltip .pelle-current-glyph-effects { + font-weight: bold; + color: var(--color-pelle--base); } .t-s8 .c-current-glyph-effects { @@ -770,21 +904,20 @@ } .c-current-glyph-effects__header { - font-weight: bold; font-size: 1.2rem; + font-weight: bold; } .l-equipped-glyphs-and-effects-container { display: flex; flex-direction: row; - justify-content: space-around; width: 65rem; + justify-content: space-around; } .l-glyph-info-wrapper { display: flex; flex-direction: column; - align-items: center; } .t-s6 .l-glyph-info-wrapper, @@ -795,16 +928,9 @@ .c-glyph-info-options { display: flex; flex-direction: row; - width: 45rem; - height: 3rem; - margin-bottom: -0.1rem; - border: 0.1rem solid #b8b8b8; - border-top-left-radius: 0.5rem; - border-top-right-radius: 0.5rem; -} - -.s-base--metro .c-glyph-info-options { - border-radius: 0; + width: 100%; + height: 2.8rem; + justify-content: space-between; } .t-s8 .c-glyph-info-options { @@ -813,28 +939,55 @@ .c-glyph-info-button { display: flex; + width: 50%; + z-index: 1; justify-content: center; align-items: center; - color: var(--color-text); + text-align: center; + font-family: Typewriter; font-size: 1.2rem; - width: 50%; + font-weight: bold; + color: var(--color-reality-dark); + background-color: var(--color-background); + border: 0.2rem solid var(--color-reality-dark); + border-radius: 0.5rem 0.5rem 0 0; + transition: all 0.2s, border-bottom 0ms, padding-bottom 0ms; cursor: pointer; - transition: font-size 0.2s; +} + +.s-base--dark .c-glyph-info-button { + color: var(--color-reality-light); } .t-s8 .c-glyph-info-button { border-color: black !important; } -.c-glyph-info-button--active { - font-size: 1.25rem; - font-weight: bold; - cursor: default; - width: 50%; +.l-glyph-info-button { + display: flex; + flex-direction: column; + width: 22rem; + height: 3rem; + justify-content: center; + align-items: center; } -.c-glyph-info-button:hover { - font-size: 1.25rem; +.s-base--metro .c-glyph-info-button { + height: 2.9rem; + border-width: 0.1rem; + border-radius: 0; +} + +.c-glyph-info-button--active { + border-bottom: none; + padding-bottom: 0.3rem; + transition-duration: 0ms; + cursor: default; +} + +.c-glyph-info-button--inactive:hover { + color: black; + background-color: var(--color-reality-light); } .l-sacrificed-glyphs { @@ -842,69 +995,65 @@ } .c-glyph-peek { - border: 0.1rem solid #b8b8b8; - border-radius: 0.5rem; - padding: 1rem; - height: 9rem; width: calc(100% - 1rem); + height: 9rem; + border: 0.1rem solid #b8b8b8; + border-radius: var(--var-border-radius, 0.5rem); + padding: 1rem; } .c-glyph-choice-container { display: flex; - flex-direction: row; - flex-wrap: wrap; + flex-flow: row wrap; width: 74rem; } .c-glyph-choice-single-glyph { display: flex; flex-direction: row; + width: 36rem; + height: 12rem; justify-content: space-evenly; align-items: center; - border-radius: 0.5rem; + border-radius: var(--var-border-radius, 0.5rem); margin: 0.5rem; - height: 12rem; - width: 36rem; } .c-glyph-choice-icon { display: flex; flex-direction: column; + width: 10rem; + height: 10rem; justify-content: space-evenly; align-items: center; - background-color: rgba(0, 0, 0, 0.1); - border: 0.2rem solid rgba(0, 0, 0, 0.15); - border-radius: 0.5rem; font-size: 1.2rem; - height: 10rem; - width: 10rem; - text-shadow: 0.1rem 0.1rem 0.1rem black, 0.1rem 0.1rem 0 black; - box-shadow: 0.3rem 0.3rem 0.3rem rgba(0, 0, 0, 0.5); + text-shadow: 0.1rem 0.1rem 0.1rem var(--color-background), 0.1rem 0.1rem 0 var(--color-background); + background-color: rgba(0, 0, 0, 10%); + border: var(--var-border-width, 0.2rem) solid rgba(0, 0, 0, 15%); + border-radius: var(--var-border-radius, 0.5rem); + box-shadow: 0.3rem 0.3rem 0.3rem rgba(0, 0, 0, 50%); } -.s-base--metro .c-glyph-choice-icon { - border-radius: 0; - border-width: 0.1rem; +.t-s6 .c-glyph-choice-icon, +.t-s10 .c-glyph-choice-icon { + border: 0.1rem solid white; + box-shadow: 0 0 0.5rem; } .c-glyph-choice-effect-list { display: flex; flex-direction: column; - justify-content: space-evenly; - background-color: rgba(0, 0, 0, 0.1); - border: 0.2rem solid rgba(0, 0, 0, 0.15); - border-radius: 0.5rem; - height: 11rem; width: 25rem; + height: 11rem; + justify-content: space-evenly; + background-color: rgba(0, 0, 0, 10%); + border: var(--var-border-width, 0.2rem) solid rgba(0, 0, 0, 15%); + border-radius: var(--var-border-radius, 0.5rem); } -.s-base--metro .c-glyph-choice-effect-list { - border-radius: 0; - border-width: 0.1rem; -} - -.s-base--metro .c-glyph-peek { - border-radius: 0; +.t-s6 .c-glyph-choice-effect-list, +.t-s10 .c-glyph-choice-effect-list { + border: 0.1rem solid white; } .t-s8 .c-glyph-peek { @@ -912,13 +1061,16 @@ } .c-sacrificed-glyphs--dragover { - border-color: rgb(0, 0, 0); - box-shadow: 0 0 0.1rem 0.1rem rgb(0, 0, 0); + z-index: 1; + border: 0.3rem solid gray; + box-shadow: 0 0 0.1rem 0.1rem black; } .s-base--dark .c-sacrificed-glyphs--dragover { - border-color: rgb(255, 255, 255); - box-shadow: 0 0 0.1rem 0.1rem rgb(255, 255, 255); + z-index: 2; + background-color: transparent; + border: 0.2rem solid white; + box-shadow: 0 0 0.1rem 0.1rem white; } .c-sacrificed-glyphs__header { @@ -958,8 +1110,13 @@ } .c-sacrificed-glyphs__confirm { - cursor: pointer; + -webkit-user-select: none; user-select: none; + cursor: pointer; +} + +.c-altered-glyphs-toggle-button { + cursor: pointer; } .l-glyph-inventory { @@ -991,154 +1148,164 @@ } .s-base--dark .c-glyph-inventory__slot { - background-color: #333; + background-color: #333333; } .c-glyph-inventory-option { width: 19rem; height: auto; - padding: 0.5rem; - margin-bottom: 0.5rem; position: relative; - font-weight: bold; - color: var(--color-reality-light); - background-color: black; - border: 0.2rem solid var(--color-reality); - border-radius: 0.5rem; - transition-duration: 0.2s; text-align: center; - font-size: 1rem; font-family: Typewriter, serif; + font-size: 1rem; + font-weight: bold; + color: var(--color-reality-dark); + background-color: var(--color-background); + border: var(--var-border-width, 0.2rem) solid var(--color-reality-dark); + border-radius: var(--var-border-radius, 0.5rem); + margin-bottom: 0.5rem; + padding: 0.5rem; + transition-duration: 0.2s; cursor: pointer; } -.c-glyph-inventory-option:hover { - color: black; - background-color: var(--color-reality-light); - border-color: var(--color-reality-light); +.s-base--dark .c-glyph-inventory-option { + color: var(--color-reality-light); } -.s-base--metro .c-glyph-inventory-option { - border-width: 0.1rem; - border-radius: 0; +.c-glyph-inventory-option:hover { + color: var(--color-text); + background-color: var(--color-reality-light); + border-color: var(--color-reality); +} + +.s-base--dark .c-glyph-inventory-option:hover { + color: var(--color-text-inverted); } .c-glyph-inventory-option__tooltip { display: flex; - justify-content: center; - align-items: center; - opacity: 0; - transition-duration: 0.2s; - font-size: 1.1rem; - font-weight: bold; - border: 0.2rem solid var(--color-reality); - border-radius: 0.5rem; - font-family: Typewriter; - color: var(--color-reality); - background: black; width: 100%; - left: 0%; - line-height: 1.8rem; - padding: 0.3rem; - z-index: 3; - pointer-events: none; position: absolute; bottom: 100%; + left: 0%; + z-index: 3; + justify-content: center; + align-items: center; + font-family: Typewriter; + font-size: 1.1rem; + font-weight: bold; + line-height: 1.8rem; + opacity: 0; + color: var(--color-reality); + background: var(--color-background); + border: var(--var-border-width, 0.2rem) solid var(--color-reality); + border-radius: var(--var-border-radius, 0.5rem); + padding: 0.3rem; + transition-duration: 0.2s; + pointer-events: none; } -.s-base--metro .c-glyph-inventory-option__tooltip { - border-width: 0.1rem; - border-radius: 0; -} - -.c-glyph-inventory-option__tooltip:after { +.c-glyph-inventory-option__tooltip::after { + content: " "; + width: 0; position: absolute; bottom: 0; left: 50%; - margin-left: -0.7rem; - margin-bottom: 0; - width: 0; - border-top: 0 solid var(--color-reality-light); + z-index: 0; + border-top: 0 solid var(--color-reality); border-right: 0.7rem solid transparent; border-left: 0.7rem solid transparent; - content: " "; + margin-bottom: 0; + margin-left: -0.7rem; transition-duration: 0.2s; - z-index: 0; +} + +.s-base--dark .c-glyph-inventory-option__tooltip::after { + border-top: 0 solid var(--color-reality-light); } .c-glyph-inventory-option:hover .c-glyph-inventory-option__tooltip { - opacity: 1; bottom: calc(100% + 0.9rem); - border-color: var(--color-reality-light); + opacity: 1; + color: var(--color-reality); + border-color: var(--color-reality); +} + +.s-base--dark .c-glyph-inventory-option:hover .c-glyph-inventory-option__tooltip { color: var(--color-reality-light); + border-color: var(--color-reality-light); } .s-base--metro .c-glyph-inventory-option:hover .c-glyph-inventory-option__tooltip { - opacity: 1; bottom: calc(100% + 0.8rem); - border-color: var(--color-reality-light); - color: var(--color-reality-light); } -.c-glyph-inventory-option:hover .c-glyph-inventory-option__tooltip:after { +.c-glyph-inventory-option:hover .c-glyph-inventory-option__tooltip::after { border-top-width: 0.7rem; margin-bottom: -0.9rem; } -.s-base--metro .c-glyph-inventory-option:hover .c-glyph-inventory-option__tooltip:after { +.s-base--metro .c-glyph-inventory-option:hover .c-glyph-inventory-option__tooltip::after { margin-bottom: -0.8rem; } .l-glyph-color-box { display: flex; position: relative; + top: 27.9rem; + left: -50%; + z-index: 2; } .l-glyph-color-position__top { - position: absolute; - right: 22.5rem; width: 0%; + position: absolute; + top: 1.1rem; + right: 22.5rem; } .l-glyph-color-position__low { - position: absolute; - top: 2.9rem; - right: 22.5rem; width: 0%; + position: absolute; + top: 3.9rem; + right: 22.5rem; } .l-glyph-sidebar-option-container { display: flex; flex-direction: column; - height: 5rem; width: 100%; + height: 5rem; } .l-glyph-sidebar-tab-container { display: flex; flex-direction: row; - justify-content: space-between; - height: 5rem; width: 100%; + height: 5rem; + justify-content: space-between; } .c-glyph-sidebar-button { - background-color: black; - border: 0.2rem solid var(--color-reality); - border-radius: 0.5rem 0.5rem 0 0; - color: var(--color-reality-light); - cursor: pointer; - transition: All 0.2s, border-bottom 0ms, padding-bottom 0ms; - text-align: center; - font-size: 1rem; - font-family: Typewriter; - font-weight: bold; z-index: 1; + text-align: center; + font-family: Typewriter; + font-size: 1rem; + font-weight: bold; + color: var(--color-reality-dark); + background-color: var(--color-background); + border: var(--var-border-width, 0.2rem) solid var(--color-reality-dark); + border-radius: var(--var-border-radius, 0.5rem) var(--var-border-radius, 0.5rem) 0 0; + transition: all 0.2s, border-bottom 0ms, padding-bottom 0ms; + cursor: pointer; +} + +.s-base--dark .c-glyph-sidebar-button { + color: var(--color-reality-light); } .s-base--metro .c-glyph-sidebar-button { - border-width: 0.1rem; - border-radius: 0; margin-bottom: 0.1rem; } @@ -1149,24 +1316,28 @@ .c-glyph-sidebar-button--active { border-bottom: none; - cursor: default; padding-bottom: 0.3rem; transition-duration: 0ms; + cursor: default; } .c-glyph-sidebar-button--active:hover { + color: var(--color-reality-dark); + background-color: var(--color-background); +} + +.s-base--dark .c-glyph-sidebar-button--active:hover { color: var(--color-reality-light); - background-color: black; } .l-glyph-sidebar-button { display: flex; flex-direction: column; + width: 100%; + height: 4rem; justify-content: center; align-items: center; - width: 100%; margin: 0 0.5rem; - height: 4rem; } .l-glyph-sidebar-panel-size { @@ -1178,39 +1349,35 @@ } @keyframes a-glyph-side-box-button-glow { - 0% { box-shadow: inset 0 0 2rem var(--color-ra-pet-effarig) } - 50% { box-shadow: inset 0 0 0 } - 100% { box-shadow: inset 0 0 2rem var(--color-ra-pet-effarig) } + 0% { box-shadow: inset 0 0 2rem var(--color-ra-pet--effarig); } + 50% { box-shadow: inset 0 0 0; } + 100% { box-shadow: inset 0 0 2rem var(--color-ra-pet--effarig); } } .o-glyph-color-checkbox { display: flex; + width: 2rem; + height: 2rem; justify-content: center; align-items: center; font-size: 1rem; - height: 2rem; - width: 2rem; - border: 0.1rem solid #b8b8b8; - border-top-left-radius: 0.3rem; - border-bottom-right-radius: 1rem; - color: black; font-weight: bold; - cursor: pointer; - user-select: none; + color: black; + border: var(--var-border-width, 0.2rem) solid var(--color-reality); + border-top-right-radius: var(--var-border-radius, 1rem); + border-bottom-left-radius: var(--var-border-radius, 0.3rem); transition-duration: 0.2s; -} - -.s-base--metro .o-glyph-color-checkbox { - border-radius: 0; + -webkit-user-select: none; + user-select: none; + cursor: pointer; } .t-s8 .o-glyph-color-checkbox { border-color: black; } - .o-glyph-color-checkbox:hover { - transform: scale(1.1) translate(0.1rem, 0.1rem); + transform: scale(1.1) translate(0.1rem, -0.1rem); } .o-glyph-color-checkbox--active { @@ -1225,212 +1392,191 @@ cursor: zoom-in; } -@keyframes a-reality-glyph-outer-cycle { - 0% { - background-color: #b67f33; - box-shadow: #b67f33 0 0 1rem 0.2rem; - } - 20% { - background-color: #64dd17; - box-shadow: #64dd17 0 0 1rem 0.2rem; - } - 40% { - background-color: #22aa48; - box-shadow: #22aa48 0 0 1rem 0.2rem; - } - 60% { - background-color: #03a9f4; - box-shadow: #03a9f4 0 0 1rem 0.2rem; - } - 80% { - background-color: #b241e3; - box-shadow: #b241e3 0 0 1rem 0.2rem; - } - 100% { - background-color: #b67f33; - box-shadow: #b67f33 0 0 1rem 0.2rem; - } -} - -@keyframes a-reality-glyph-over-cycle { - 0% { - box-shadow: #b67f33 0 0 1rem calc(0.3rem) inset; - } - 20% { - box-shadow: #64dd17 0 0 1rem calc(0.3rem) inset; - } - 40% { - box-shadow: #22aa48 0 0 1rem calc(0.3rem) inset; - } - 60% { - box-shadow: #03a9f4 0 0 1rem calc(0.3rem) inset; - } - 80% { - box-shadow: #b241e3 0 0 1rem calc(0.3rem) inset; - } - 100% { - box-shadow: #b67f33 0 0 1rem calc(0.3rem) inset; - } -} - -@keyframes a-reality-glyph-icon-cycle { - 0% { - color: #b67f33; - text-shadow: #b67f33 -0.04em 0.04em 0.08em; - } - 20% { - color: #64dd17; - text-shadow: #64dd17 -0.04em 0.04em 0.08em; - } - 40% { - color: #22aa48; - text-shadow: #22aa48 -0.04em 0.04em 0.08em; - } - 60% { - color: #03a9f4; - text-shadow: #03a9f4 -0.04em 0.04em 0.08em; - } - 80% { - color: #b241e3; - text-shadow: #b241e3 -0.04em 0.04em 0.08em; - } - 100% { - color: #b67f33; - text-shadow: #b67f33 -0.04em 0.04em 0.08em; - } -} - @keyframes a-reality-glyph-name-cycle { 0% { color: #b67f33; - text-shadow: black -1px 1px 1px, black 1px 1px 1px, black -1px -1px 1px, black 1px -1px 1px, #b67f33 0 0 3px + text-shadow: + var(--color-text-inverted) -1px 1px 1px, + var(--color-text-inverted) 1px 1px 1px, + var(--color-text-inverted) -1px -1px 1px, + var(--color-text-inverted) 1px -1px 1px, + #b67f33 0 0 3px; } + 20% { color: #64dd17; - text-shadow: black -1px 1px 1px, black 1px 1px 1px, black -1px -1px 1px, black 1px -1px 1px, #64dd17 0 0 3px + text-shadow: + var(--color-text-inverted) -1px 1px 1px, + var(--color-text-inverted) 1px 1px 1px, + var(--color-text-inverted) -1px -1px 1px, + var(--color-text-inverted) 1px -1px 1px, + #64dd17 0 0 3px; } + 40% { color: #22aa48; - text-shadow: black -1px 1px 1px, black 1px 1px 1px, black -1px -1px 1px, black 1px -1px 1px, #22aa48 0 0 3px + text-shadow: + var(--color-text-inverted) -1px 1px 1px, + var(--color-text-inverted) 1px 1px 1px, + var(--color-text-inverted) -1px -1px 1px, + var(--color-text-inverted) 1px -1px 1px, + #22aa48 0 0 3px; } + 60% { color: #03a9f4; - text-shadow: black -1px 1px 1px, black 1px 1px 1px, black -1px -1px 1px, black 1px -1px 1px, #03a9f4 0 0 3px + text-shadow: + var(--color-text-inverted) -1px 1px 1px, + var(--color-text-inverted) 1px 1px 1px, + var(--color-text-inverted) -1px -1px 1px, + var(--color-text-inverted) 1px -1px 1px, + #03a9f4 0 0 3px; } + 80% { color: #b241e3; - text-shadow: black -1px 1px 1px, black 1px 1px 1px, black -1px -1px 1px, black 1px -1px 1px, #b241e3 0 0 3px + text-shadow: + var(--color-text-inverted) -1px 1px 1px, + var(--color-text-inverted) 1px 1px 1px, + var(--color-text-inverted) -1px -1px 1px, + var(--color-text-inverted) 1px -1px 1px, + #b241e3 0 0 3px; } + 100% { color: #b67f33; - text-shadow: black -1px 1px 1px, black 1px 1px 1px, black -1px -1px 1px, black 1px -1px 1px, #b67f33 0 0 3px + text-shadow: + var(--color-text-inverted) -1px 1px 1px, + var(--color-text-inverted) 1px 1px 1px, + var(--color-text-inverted) -1px -1px 1px, + var(--color-text-inverted) 1px -1px 1px, + #b67f33 0 0 3px; } } @keyframes a-reality-glyph-description-cycle { 0% { color: #b67f33; - text-shadow: var(--color-text-base) -1px 1px 1px, var(--color-text-base) - 1px 1px 1px, var(--color-text-base) -1px -1px 1px, var(--color-text-base) 1px -1px 1px, #b67f33 0 0 3px + text-shadow: + var(--color-text-base) -1px 1px 1px, + var(--color-text-base) 1px 1px 1px, + var(--color-text-base) -1px -1px 1px, + var(--color-text-base) 1px -1px 1px, + #b67f33 0 0 3px; } + 20% { color: #64dd17; - text-shadow: var(--color-text-base) -1px 1px 1px, var(--color-text-base) - 1px 1px 1px, var(--color-text-base) -1px -1px 1px, var(--color-text-base) 1px -1px 1px, #64dd17 0 0 3px + text-shadow: + var(--color-text-base) -1px 1px 1px, + var(--color-text-base) 1px 1px 1px, + var(--color-text-base) -1px -1px 1px, + var(--color-text-base) 1px -1px 1px, + #64dd17 0 0 3px; } + 40% { color: #22aa48; - text-shadow: var(--color-text-base) -1px 1px 1px, var(--color-text-base) - 1px 1px 1px, var(--color-text-base) -1px -1px 1px, var(--color-text-base) 1px -1px 1px, #22aa48 0 0 3px + text-shadow: + var(--color-text-base) -1px 1px 1px, + var(--color-text-base) 1px 1px 1px, + var(--color-text-base) -1px -1px 1px, + var(--color-text-base) 1px -1px 1px, + #22aa48 0 0 3px; } + 60% { color: #03a9f4; - text-shadow: var(--color-text-base) -1px 1px 1px, var(--color-text-base) - 1px 1px 1px, var(--color-text-base) -1px -1px 1px, var(--color-text-base) 1px -1px 1px, #03a9f4 0 0 3px + text-shadow: + var(--color-text-base) -1px 1px 1px, + var(--color-text-base) 1px 1px 1px, + var(--color-text-base) -1px -1px 1px, + var(--color-text-base) 1px -1px 1px, + #03a9f4 0 0 3px; } + 80% { color: #b241e3; - text-shadow: var(--color-text-base) -1px 1px 1px, var(--color-text-base) - 1px 1px 1px, var(--color-text-base) -1px -1px 1px, var(--color-text-base) 1px -1px 1px, #b241e3 0 0 3px + text-shadow: + var(--color-text-base) -1px 1px 1px, + var(--color-text-base) 1px 1px 1px, + var(--color-text-base) -1px -1px 1px, + var(--color-text-base) 1px -1px 1px, + #b241e3 0 0 3px; } + 100% { color: #b67f33; - text-shadow: var(--color-text-base) -1px 1px 1px, var(--color-text-base) - 1px 1px 1px, var(--color-text-base) -1px -1px 1px, var(--color-text-base) 1px -1px 1px, #b67f33 0 0 3px - } -} - -@keyframes a-reality-glyph-dot-cycle { - 0% { - background: #b67f33; - } - 20% { - background: #64dd17; - } - 40% { - background: #22aa48; - } - 60% { - background: #03a9f4; - } - 80% { - background: #b241e3; - } - 100% { - background: #b67f33; + text-shadow: + var(--color-text-base) -1px 1px 1px, + var(--color-text-base) 1px 1px 1px, + var(--color-text-base) -1px -1px 1px, + var(--color-text-base) 1px -1px 1px, + #b67f33 0 0 3px; } } @keyframes a-reality-glyph-tooltip-cycle { 0% { border-color: #b67f33; - box-shadow: 0 0 0.5rem #b67f33, 0 0 0.5rem #b67f33 inset + box-shadow: 0 0 0.5rem #b67f33, 0 0 0.5rem #b67f33 inset; } + 20% { border-color: #64dd17; - box-shadow: 0 0 0.5rem #64dd17, 0 0 0.5rem #64dd17 inset + box-shadow: 0 0 0.5rem #64dd17, 0 0 0.5rem #64dd17 inset; } + 40% { border-color: #22aa48; - box-shadow: 0 0 0.5rem #22aa48, 0 0 0.5rem #22aa48 inset + box-shadow: 0 0 0.5rem #22aa48, 0 0 0.5rem #22aa48 inset; } + 60% { border-color: #03a9f4; - box-shadow: 0 0 0.5rem #03a9f4, 0 0 0.5rem #03a9f4 inset + box-shadow: 0 0 0.5rem #03a9f4, 0 0 0.5rem #03a9f4 inset; } + 80% { border-color: #b241e3; - box-shadow: 0 0 0.5rem #b241e3, 0 0 0.5rem #b241e3 inset + box-shadow: 0 0 0.5rem #b241e3, 0 0 0.5rem #b241e3 inset; } + 100% { border-color: #b67f33; - box-shadow: 0 0 0.5rem #b67f33, 0 0 0.5rem #b67f33 inset + box-shadow: 0 0 0.5rem #b67f33, 0 0 0.5rem #b67f33 inset; } } @keyframes a-reality-glyph-tooltip-header-cycle { 0% { border-color: #b67f33; - box-shadow: 0 0 0.5rem 0.1rem #b67f33, 0 0 0.8rem #b67f33 inset + box-shadow: 0 0 0.5rem 0.1rem #b67f33, 0 0 0.8rem #b67f33 inset; } + 20% { border-color: #64dd17; - box-shadow: 0 0 0.5rem 0.1rem #64dd17, 0 0 0.8rem #64dd17 inset + box-shadow: 0 0 0.5rem 0.1rem #64dd17, 0 0 0.8rem #64dd17 inset; } + 40% { border-color: #22aa48; - box-shadow: 0 0 0.5rem 0.1rem #22aa48, 0 0 0.8rem #22aa48 inset + box-shadow: 0 0 0.5rem 0.1rem #22aa48, 0 0 0.8rem #22aa48 inset; } + 60% { border-color: #03a9f4; - box-shadow: 0 0 0.5rem 0.1rem #03a9f4, 0 0 0.8rem #03a9f4 inset + box-shadow: 0 0 0.5rem 0.1rem #03a9f4, 0 0 0.8rem #03a9f4 inset; } + 80% { border-color: #b241e3; - box-shadow: 0 0 0.5rem 0.1rem #b241e3, 0 0 0.8rem #b241e3 inset + box-shadow: 0 0 0.5rem 0.1rem #b241e3, 0 0 0.8rem #b241e3 inset; } + 100% { border-color: #b67f33; - box-shadow: 0 0 0.5rem 0.1rem #b67f33, 0 0 0.8rem #b67f33 inset + box-shadow: 0 0 0.5rem 0.1rem #b67f33, 0 0 0.8rem #b67f33 inset; } } +} \ No newline at end of file diff --git a/public/stylesheets/new-ui-styles.css b/public/stylesheets/new-ui-styles.css index 7f8f9092e..e49a2bced 100644 --- a/public/stylesheets/new-ui-styles.css +++ b/public/stylesheets/new-ui-styles.css @@ -1,20 +1,25 @@ @font-face { font-family: Typewriter; - src: url('MonospaceTypewriter.ttf'); + src: url("MonospaceTypewriter.ttf"); } *:focus { outline: none; } -.t-normal #ui-container, -.t-s9 #ui-container { - --color-text: white; +:root { + --sidebar-width: 12.8rem; +} + +:root .t-normal, +:root .t-s9 { + --color-text: #ffffff; --color-text-inverted: black; --color-base: #1d1b22; --color-accent: #df5050; --color-good: #1bbb36; --color-good-dark: #138626; + --color-notification: yellow; --color-antimatter: #df5050; @@ -32,38 +37,46 @@ body.t-s9 { background-color: #111014; } +.t-normal .c-background-overlay, +.t-s9 .c-background-overlay { + background: black; + background-position: center; + background-size: 100%; +} + #page { - margin: auto; display: flex; + width: 100%; + height: 100%; justify-content: flex-start; align-items: stretch; font-family: Typewriter, serif; background: none; - height: 100%; - width: 100%; + margin: auto; } -.sidebar { +.c-modern-sidebar { display: flex; flex-direction: column; - width: 12rem; - position: fixed; + width: var(--sidebar-width); + position: absolute; left: 0; z-index: 5; + pointer-events: auto; } -.sidebar:hover .subtabs, -.sidebar:hover .l-tab-btn-inner, -.sidebar:hover .o-tab-btn, -.sidebar:hover .o-tab-btn::after { +.c-modern-sidebar:hover .subtabs, +.c-modern-sidebar:hover .l-tab-btn-inner, +.c-modern-sidebar:hover .o-tab-btn, +.c-modern-sidebar:hover .o-tab-btn::after { transition-delay: 0s; } -.resource { - height: 7rem; - width: 12rem; +.c-sidebar-resource { display: flex; flex-direction: column; + width: var(--sidebar-width); + height: 7rem; justify-content: center; align-items: center; background-color: var(--color-base); @@ -72,33 +85,37 @@ body.t-s9 { padding: 1rem; } -.resource h2 { - margin: 0; - font-size: 1.7em; +.c-sidebar-resource:last-child { + border-bottom-right-radius: var(--var-border-radius, 0.5rem); +} + +.c-sidebar-resource h2 { z-index: 1; + font-size: 1.9rem; + margin: 0; } -.t-dark .resource h2, -.t-dark-metro .resource h2 { - text-shadow: 0 0 0.1rem rgba(0, 0, 0, 0.5), -0.1rem 0.1rem 0.1rem rgba(0, 0, 0, 0.8); +.t-dark .c-sidebar-resource h2, +.t-dark-metro .c-sidebar-resource h2 { + text-shadow: 0 0 0.1rem rgba(0, 0, 0, 50%), -0.1rem 0.1rem 0.1rem rgba(0, 0, 0, 80%); } -.resource-information { +.c-sidebar-resource__information { display: flex; flex-direction: column; - align-items: flex-start; - color: var(--color-text); justify-content: space-between; - font-size: 1.5em; + align-items: flex-start; + font-size: 1.5rem; + color: var(--color-text); } .resource-gain { - font-size: 0.7em; + font-size: 0.7rem; color: var(--color-text); } -.resource-name { - font-size: 0.8em; +.c-sidebar-resource__name { + font-size: 1.2rem; } .o-sidebar-currency--antimatter { @@ -123,37 +140,37 @@ body.t-s9 { color: #e0e0e0; } -.o-sidebar-currency--infinity { - color: var(--color-infinity) +.o-sidebar-currency--infinity { + color: var(--color-infinity); } -.o-sidebar-currency--eternity { - color: var(--color-eternity) +.o-sidebar-currency--eternity { + color: var(--color-eternity); } .o-sidebar-currency--reality { - color: var(--color-reality) + color: var(--color-reality); } .t-dark .o-sidebar-currency--reality, .t-dark-metro .o-sidebar-currency--reality { - color: var(--color-reality) + color: var(--color-reality); } .o-sidebar-currency--pelle { - color: var(--color-pelle--base) + color: var(--color-pelle--base); } .resource-infinity-canreset, .resource-eternity-canreset { background-color: black; - cursor: pointer; transition-duration: 0.15s; + cursor: pointer; } .resource-infinity-canreset:hover, .resource-eternity-canreset:hover { - background-color: rgb(49, 49, 49) + background-color: rgb(49, 49, 49); } .t-normal .c-modal { @@ -163,52 +180,53 @@ body.t-s9 { .o-tab-btn { height: 4.6rem; + position: relative; + font-size: 1.4rem; border-width: 0.1rem; border-radius: 0; margin-top: -0.1rem; - font-size: 1.3em; - position: relative; - transition-duration: 0.15s; transition-delay: 0.2s; + transition-duration: 0.15s; } .o-tab-btn:hover { z-index: 1; } -.sidebar > .o-tab-btn { - border-left: none; +.c-modern-sidebar > .o-tab-btn { + border-left-width: 0; } .l-tab-btn-inner { - width: 100%; - height: 100%; + position: relative; display: flex; flex-direction: column; + width: 100%; + height: 100%; justify-content: center; align-items: center; cursor: pointer; } .o-tab-btn:hover .subtabs { + left: calc(var(--sidebar-width) + 2.3rem); opacity: 1; pointer-events: all; - left: 14.3rem; } .o-tab-btn::after { - content: ''; - position: absolute; - left: calc(100% + 0.1rem); - top: calc(0% + -0.1rem); + content: ""; width: 0; height: 0; + position: absolute; + top: calc(0% + -0.1rem); + left: calc(100% + 0.1rem); + z-index: 4; + border-top: 2.3rem solid transparent; border-bottom: 2.3rem solid transparent; border-left: 0 solid var(--color-accent); - border-top: 2.3rem solid transparent; - z-index: 4; - transition-duration: 0.15s; transition-delay: 0.2s; + transition-duration: 0.15s; } .o-tab-btn--subtabs:hover::after { @@ -240,24 +258,19 @@ body.t-s9 { } .game-container { - width: calc(100% - 12rem); - margin-left: 12rem; + width: calc(100% - var(--sidebar-width)); + margin-left: var(--sidebar-width); } .t-s4 .game-container { border-radius: 0 !important; } -.l-reset-buttons-container { - position: relative; - min-height: 8rem; -} - .tab-container { display: flex; - justify-content: center; flex-direction: column; position: relative; + justify-content: center; } .tab-container > .l-antimatter-dim-tab { @@ -276,113 +289,116 @@ body.t-s9 { .subtabs { display: flex; - opacity: 0; - pointer-events: none; position: absolute; top: -0.6rem; - left: 12rem; + left: var(--sidebar-width); z-index: 4; - transition: pointer-events 0s linear 0s, all 0.15s linear 0.2s; + opacity: 0; padding: 0.5rem; padding-left: 0; + transition: pointer-events 0s linear 0s, all 0.15s linear 0.2s; + pointer-events: none; } .o-tab-btn--subtab { - height: 4.7rem; width: 4.7rem; + height: 4.7rem; font-size: 2.3rem; font-weight: bold; - margin-left: -0.1rem; line-height: 4.5rem; - cursor: pointer; + margin-left: -0.1rem; transition-duration: 0.15s; + -webkit-user-select: none; user-select: none; + cursor: pointer; } -.o-tab-btn--subtab .fa-link { - transform: rotate(45deg) +.o-tab-btn--subtab .o-tab-btn--cel3 { + font-weight: normal; + transform: rotate(45deg); } .o-subtab__tooltip { - opacity: 0; - transition-duration: 0.2s; - font-size: 1.4rem; - font-weight: bold; - border: 0.1rem solid var(--color-accent); - color: var(--color-text); - background: var(--color-base); width: 20rem; height: 3rem; - line-height: 2.8rem; - margin-left: -7.7rem; - z-index: 2; - pointer-events: none; position: absolute; bottom: 100%; + z-index: 2; + font-size: 1.4rem; + font-weight: bold; + line-height: 2.8rem; + opacity: 0; + color: var(--color-text); + background: var(--color-base); + border: 0.1rem solid var(--color-accent); + border-radius: var(--var-border-radius, 0.4rem); + margin-left: -7.7rem; + transition-duration: 0.2s; + pointer-events: none; } -.o-subtab__tooltip:after { +.o-subtab__tooltip::after { + content: " "; + width: 0; position: absolute; bottom: 0; left: 50%; - margin-left: -0.7rem; - margin-bottom: 0; - width: 0; + z-index: 0; border-top: 0 solid var(--color-accent); border-right: 0.7rem solid transparent; border-left: 0.7rem solid transparent; - content: " "; + margin-bottom: 0; + margin-left: -0.7rem; transition-duration: 0.2s; - z-index: 0; } .o-tab-btn--subtab:hover .o-subtab__tooltip { + bottom: calc(100% + 0.7rem); opacity: 1; - bottom: calc(100% + 0.7rem) } -.o-tab-btn--subtab:hover .o-subtab__tooltip:after { +.o-tab-btn--subtab:hover .o-subtab__tooltip::after { border-top-width: 0.7rem; margin-bottom: -0.7rem; } .o-tab-btn--infinity .o-subtab__tooltip { color: var(--color-infinity); - background: var(--color-prestige--accent); border-color: var(--color-infinity); + background: var(--color-prestige--accent); } -.o-tab-btn--infinity .o-subtab__tooltip:after { +.o-tab-btn--infinity .o-subtab__tooltip::after { border-top-color: var(--color-infinity); } .o-tab-btn--eternity .o-subtab__tooltip { color: var(--color-eternity); - background: var(--color-prestige--accent); border-color: var(--color-eternity); + background: var(--color-prestige--accent); } -.o-tab-btn--eternity .o-subtab__tooltip:after { +.o-tab-btn--eternity .o-subtab__tooltip::after { border-top-color: var(--color-eternity); } .o-tab-btn--reality .o-subtab__tooltip { color: var(--color-reality); - background: var(--color-prestige--accent); border-color: var(--color-reality); + background: var(--color-prestige--accent); } -.o-tab-btn--reality .o-subtab__tooltip:after { +.o-tab-btn--reality .o-subtab__tooltip::after { border-top-color: var(--color-reality); } .o-tab-btn--celestial .o-subtab__tooltip { color: var(--color-celestials); - background: var(--color-prestige--accent); border-color: var(--color-celestials); + background: var(--color-prestige--accent); } -.o-tab-btn--celestial .o-subtab__tooltip:after { +.o-tab-btn--celestial .o-subtab__tooltip::after { border-top-color: var(--color-celestials); } @@ -395,92 +411,95 @@ body.t-s9 { } .o-primary-btn--new { + overflow: hidden; + grid-column: 1 / 11; + width: 25rem; + height: 4.4rem; + position: relative; + font-family: Typewriter, serif; + color: var(--color-text); background-color: var(--color-base); border: 0.1rem solid var(--color-good); + border-radius: var(--var-border-radius, 0.5rem); + margin: 0.5rem; padding: 1rem; - color: var(--color-text); - font-family: Typewriter, serif; - border-radius: 0.5rem; - position: relative; - width: 21rem; - height: 4.4rem; - overflow: hidden; } .o-primary-btn--dimension-reset { display: flex; - align-items: center; - justify-content: center; + width: 25rem; height: 6rem; + justify-content: center; + align-items: center; font-size: 1rem; } .button-content { + display: flex; + width: 100%; + height: 100%; position: absolute; top: 0; left: 0; - height: 100%; - width: 100%; - display: flex; + z-index: 1; justify-content: center; align-items: center; - z-index: 1; } .fill { + width: 100%; + height: 100%; position: absolute; top: 0; left: 0; - height: 100%; - width: 100%; } .fill-purchased { - height: 100%; - width: 20%; - background-color: var(--color-good); float: left; + width: 20%; + height: 100%; + background-color: var(--color-good); } .fill-possible { - height: 100%; - width: 40%; - background-color: var(--color-good-dark); float: left; + width: 40%; + height: 100%; + background-color: var(--color-good-dark); } .modes-container { - width: 100%; display: flex; + width: 100%; justify-content: space-between; - padding: 0 5rem; margin-top: 1rem; + padding: 0 5rem; } .resets-container { display: flex; - justify-content: space-around; height: 16rem; + justify-content: space-around; } .reset-container h4 { - margin: 0; font-size: 1.4rem; + margin: 0; } .reset-container { - width: 40%; display: flex; flex-direction: column; - align-items: center; - justify-content: space-between; + width: 40%; height: 16rem; + justify-content: space-between; + align-items: center; margin-top: 0.3rem; } .l-tickspeed-container { display: flex; - justify-content: space-between; + justify-content: center; padding: 1rem 5rem 0 9rem; } @@ -493,34 +512,34 @@ body.t-s9 { } .tickspeed-btn { - padding: 0.5rem; + height: 3rem; min-width: 15rem; max-width: 20rem; - height: 3rem; font-size: 1.1rem; + padding: 0.5rem; } .tickspeed-max-btn { - padding: 0.5rem; width: 10rem; height: 3rem; + padding: 0.5rem; } .tickspeed-labels span { - color: var(--color-text);; height: 3rem; + color: var(--color-text); padding: 0.5rem 1rem; } .particle { - position: absolute; - color: var(--color-text);; - height: 1rem; width: 1rem; + height: 1rem; + position: absolute; + z-index: 0; + color: var(--color-text); background-image: url("../icon.png"); background-position: center; background-size: cover; - z-index: 0; } .sacrifice-btn { @@ -530,48 +549,47 @@ body.t-s9 { } .btn-big-crunch { - padding: 2rem; - font-size: 5rem; - border-radius: 0.5rem; - border: 0.2rem solid var(--color-infinity); - background-color: black; - color: var(--color-infinity); + z-index: 1; font-family: Typewriter; + font-size: 5rem; + color: var(--color-infinity); + background-color: black; + border: var(--var-border-width, 0.2rem) solid var(--color-infinity); + border-radius: var(--var-border-radius, 0.5rem); + padding: 2rem; transition-duration: 0.15s; cursor: pointer; - z-index: 1; } .btn-big-crunch--small { position: absolute; top: 4.3rem; left: calc(50% - 0.6rem); - transform: translateX(-50%); font-size: 3rem; + transform: translateX(-50%); } .btn-big-crunch:hover { - background-color: var(--color-infinity); color: black; + background-color: var(--color-infinity); } .t-normal .c-game-header__tesseract-available { - background: #EEEEEE; + background: #eeeeee; animation: a-tesseract-shift-dark 5s infinite; } - .l-new-ui-big-crunch__container { margin-top: 2rem; } .information-header { + font-size: 1.2rem; + font-weight: bold; color: var(--color-text); + border-bottom: 0.1rem solid var(--color-good); margin-top: 1rem; margin-bottom: 0.5rem; - font-weight: bold; - font-size: 1.1em; - border-bottom: 0.1rem solid var(--color-good); padding-bottom: 0.6rem; } @@ -582,29 +600,32 @@ body.t-s9 { } .t-normal .o-achievement__tooltip { - border: 0.1rem solid var(--color-accent); background: var(--color-base); + border: 0.1rem solid var(--color-accent); } -.t-normal .o-achievement__tooltip:after { +.t-normal .o-achievement__tooltip::after { border-top-color: var(--color-accent); } .o-infinity-upgrade-btn--unavailable { - background: #525252; color: white; + background: #525252; border: 0.1rem solid var(--color-accent); cursor: default; } .o-infinity-upgrade-btn--unavailable:hover { - background: #525252; color: var(--color-text-inverted); + background: #525252; } -.o-primary-btn--option, .o-primary-btn--option-wide, .c-select-theme__item, .c-select-notation__item { - background: var(--color-base); +.o-primary-btn--option, +.o-primary-btn--option-wide, +.c-select-theme__item, +.c-select-notation__item { color: var(--color-text); + background: var(--color-base); border: 0.1rem solid var(--color-good); } @@ -639,23 +660,21 @@ body.t-s9 { } .c-challenge-box--normal { - color: var(--color-text); - background: var(--color-base); - border: 0.1rem solid var(--color-accent); + border-color: var(--color-accent); } .o-autobuyer-input, .c-autobuyer-box__mode-select { + color: var(--color-accent); background: var(--color-base); border: 0.1rem solid var(--color-accent); - color: var(--color-accent); } .t-metro .o-autobuyer-input, .t-metro .c-autobuyer-box__mode-select { color: black; - border: 0.1rem solid #A9A9A9; background: white; + border: 0.1rem solid #a9a9a9; } .t-dark .o-autobuyer-input, @@ -663,7 +682,7 @@ body.t-s9 { .t-dark .c-autobuyer-box__mode-select, .t-dark-metro .c-autobuyer-box__mode-select { color: black; - background-color: #455A64; + background-color: #455a64; border: 0.1rem solid black; } @@ -679,43 +698,42 @@ body.t-s9 { .t-s10 .c-autobuyer-box__mode-select { color: white; background-color: black; - border: 0.1rem solid #888; + border: 0.1rem solid #888888; } .o-autobuyer-btn { border: 0.1rem solid var(--color-accent); } -.c-replicanti-description__accent { - color: #2196f3 -} - .c-dim-row__name { + flex-grow: 1; + width: 55rem; font-size: 2rem; } -.c-antimatter-dim-row, -.c-infinity-dim-row, -.c-time-dim-row { - color: var(--color-text); +.c-dimension-row { font-weight: bold; + color: var(--color-text); + border-radius: var(--var-border-radius, 0.5rem); margin: 0; padding: 0.5rem 0 0.5rem 1rem; - border-radius: 0.5rem; } -.c-antimatter-dim-row:nth-child(even) { - background-color: rgba(223, 80, 80, 0.3); +.l-dimension-single-row { + display: grid; + grid-template-columns: repeat(7, 1fr); + height: 5.5rem; + align-content: center; } -.t-metro .c-antimatter-dim-row:nth-child(even), -.t-inverted-metro .c-antimatter-dim-row:nth-child(even), -.t-s8 .c-antimatter-dim-row:nth-child(even) { - background-color: rgba(33, 150, 243, 0.3); +.c-dimension-row:nth-child(even) { + background-color: rgba(223, 80, 80, 30%); } -.c-antimatter-dim-row__multiplier { - font-size: 1.4rem +.t-metro .c-dimension-row:nth-child(even), +.t-inverted-metro .c-dimension-row:nth-child(even), +.t-s8 .c-dimension-row:nth-child(even) { + background-color: rgba(33, 150, 243, 30%); } .c-infinity-dim-description__accent { @@ -723,19 +741,29 @@ body.t-s9 { } .c-infinity-dim-row:nth-child(even) { - background: rgba(182, 127, 51, 0.3); + background: rgba(182, 127, 51, 30%); } .s-base--metro .c-infinity-dim-row:nth-child(even) { - background: rgba(255, 152, 0, 0.3); + background: rgba(255, 152, 0, 30%); } .c-infinity-dim-row__multiplier { - font-size: 1.4rem + font-size: 1.4rem; } -.o-primary-btn--buy-id-max, -.o-primary-btn--id-autobuyer { +.o-primary-btn--buy-dim { + max-width: 25rem; +} + +.o-primary-btn--buy-id { + width: 30rem; + height: 4.4rem; + margin: 0 0.5rem; +} + +.o-primary-btn--id-auto { + width: 9rem; height: 4.4rem; } @@ -744,23 +772,29 @@ body.t-s9 { } .c-time-dim-row:nth-child(even) { - background: rgba(179, 65, 224, 0.3); + background: rgba(179, 65, 224, 30%); } .s-base--metro .c-time-dim-row:nth-child(even) { - background: rgba(103, 58, 183, 0.3); + background: rgba(103, 58, 183, 30%); } .c-time-dim-row__multiplier { - font-size: 1.4rem + font-size: 1.4rem; } -.o-primary-btn--buy-td-max, -.o-primary-btn--td-autobuyer { +.o-primary-btn--buy-td { + width: 30rem; + height: 4.4rem; + margin: 0 0.5rem; +} + +.o-primary-btn--buy-td-auto { + width: 9rem; height: 4.4rem; } - .l-reality-upgrade-btn { +.l-reality-upgrade-btn { width: 18rem; margin: 0.6rem 0.4rem; } @@ -785,11 +819,11 @@ body.t-s9 { } .o-eternity-button__eternity-text { - color: var(--color-eternity); display: flex; + height: 100%; justify-content: center; align-items: center; - height: 100%; + color: var(--color-eternity); padding: 0 1rem; } @@ -797,6 +831,7 @@ body.t-s9 { top: 15%; left: 72%; } + .l-game-header__eternity-btn { top: 15%; right: 72%; @@ -808,16 +843,15 @@ body.t-s9 { } .l-game-header__infinity-points { - left: 72%; top: 8.5rem; + left: 72%; } .l-game-header__eternity-points { - right: 72%; top: 8.5rem; + right: 72%; } - .l-reset-buttons-container__reality-button { margin-top: 1.7rem; margin-bottom: 0; @@ -828,9 +862,10 @@ body.t-s9 { } .l-reset-buttons-container { + width: 100%; + min-height: 8rem; position: absolute; top: 0; - width: 100%; } .l-game-header__antimatter-container { @@ -854,6 +889,10 @@ body.t-s9 { color: var(--color-accent); } +.t-normal .c-credits-header { + color: var(--color-accent); +} + .c-dilation-tab__tachyons, .c-dilation-tab__dilated-time, .c-dilation-tab__dilated-time-income, @@ -885,29 +924,24 @@ body.t-s9 { } .t-normal .c-glyph-choice-icon { - - background-color: rgba(0, 0, 0, 0.3); - border: 0.2rem solid rgba(0, 0, 0, 0.4); + background-color: rgba(0, 0, 0, 30%); + border: var(--var-border-width, 0.2rem) solid rgba(0, 0, 0, 40%); } .t-normal .c-glyph-choice-effect-list { - background-color: rgba(0, 0, 0, 0.3); - border: 0.2rem solid rgba(0, 0, 0, 0.4); + background-color: rgba(0, 0, 0, 30%); + border: var(--var-border-width, 0.2rem) solid rgba(0, 0, 0, 40%); } .l-help-me { + height: 2rem; position: absolute; top: 4.9rem; - height: 2rem; - border-radius: 50%; font-size: 1.5rem; + border-radius: var(--var-border-radius, 50%); transition-delay: 0s; } .l-information { top: 7rem; } - -.s-base--metro .l-help-me { - border-radius: 0; -} diff --git a/public/stylesheets/old-ui.css b/public/stylesheets/old-ui.css index f72c40916..843f378cd 100644 --- a/public/stylesheets/old-ui.css +++ b/public/stylesheets/old-ui.css @@ -1,8 +1,12 @@ +#ui { + scrollbar-gutter: stable both-edges; +} + .c-old-ui { - padding: 0.9rem 1.5rem 0; - box-sizing: border-box; width: 100%; height: 100%; + box-sizing: border-box; + padding: 0.9rem 1.5rem 0; } .l-old-ui { @@ -21,8 +25,8 @@ } .l-old-ui__big-crunch-btn { - margin-top: 1.9rem; flex-shrink: 0; + margin-top: 1.9rem; } .l-old-ui__big-crunch-btn--overlay { @@ -30,15 +34,11 @@ } .l-old-ui__page { - width: 100%; - flex: 1 0 auto; -} - -.l-old-ui-page { display: flex; + flex: 1 0 auto; flex-direction: column; - align-items: stretch; width: 100%; + align-items: stretch; } .o-emptiness { @@ -46,24 +46,22 @@ } .o-big-crunch-btn { - font-family: Typewriter, serif; - font-size: 5rem; width: 36rem; height: 10rem; z-index: 1; + font-family: Typewriter, serif; + font-size: 5rem; } .l-old-ui-antimatter-dim-tab { - height: 100%; display: flex; flex-direction: column; + height: 100%; align-items: center; } .s-base--metro .o-tab-btn { - border-width: 0.1rem; - box-shadow: 0.1rem 0.1rem 0.1rem 0 #9E9E9E; - border-radius: 0; + box-shadow: 0.1rem 0.1rem 0.1rem 0 #9e9e9e; } .t-dark-metro .o-tab-btn { @@ -71,26 +69,26 @@ } .t-s4 .o-tab-btn { - cursor: url(cursor2.cur), auto; + cursor: url("cursor2.cur"), auto; } .t-s8 .o-tab-btn { box-shadow: none; } +.t-s10 .o-tab-btn { + box-shadow: none; +} + .o-tab-btn--secondary { - font-size: 1.3rem; - width: 17.5rem; + width: 18.5rem; height: 2.5rem; + font-size: 1.3rem; margin: 0.5rem 0.8rem; margin-bottom: 0.8rem; } .c-subtab-button-container { - border-bottom: 0.2rem solid var(--color-accent); + border-bottom: var(--var-border-width, 0.2rem) solid var(--color-accent); margin-bottom: 0.8rem; } - -.s-base--metro .c-subtab-button-container { - border-width: 0.1rem; -} diff --git a/public/stylesheets/styles.css b/public/stylesheets/styles.css index f6f1a453a..046ded957 100644 --- a/public/stylesheets/styles.css +++ b/public/stylesheets/styles.css @@ -1,8 +1,29 @@ html { - font-size: 62.5%; - height: 100%; - width: 100%; overflow: hidden; + width: 100%; + height: 100%; + scrollbar-color: grey transparent; + font-size: 62.5%; +} + +::-webkit-scrollbar { + width: 1.6rem; +} + +::-webkit-scrollbar-thumb { + background-clip: content-box; + background-color: grey; + border: 0.2rem solid transparent; + border-radius: var(--var-border-radius, 0.5rem); +} + +::-webkit-scrollbar-corner { + -webkit-appearance: none; + appearance: none; +} + +.s-base--metro::-webkit-scrollbar-thumb { + border-radius: 0; } ._kong-test { @@ -10,38 +31,44 @@ html { background: white; } -* { /* csslint allow: universal-selector */ - box-sizing: border-box; +* { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + box-sizing: border-box; } /* css vars */ #ui { display: flex; - align-items: center; justify-content: center; + align-items: center; + + scrollbar-gutter: stable; } -#ui-container { - --color-text: black; +:root { + /* Don't change this instance of --color-text or any of its values in other themes to not be hex; it's parsed as hex + elsewhere to provide theme-dependent color gradients */ + --color-text: #000000; --color-text-inverted: white; - --color-base: #F2F2F2; - --color-disabled: #A3A3A3; - --color-accent: #4980CC; - --color-good: #5AC467; - --color-good-dark: #127A20; + --color-base: #f2f2f2; + --color-disabled: #a3a3a3; + --color-accent: #4980cc; + --color-good: #5ac467; + --color-good-dark: #127a20; --color-good-paused: #e3e638; - --color-bad: #B84B5F; - --color-gh-purple: #8957E5; + --color-bad: #b84b5f; + --color-gh-purple: #8957e5; + --color-notification: red; --color-antimatter: #2196f3; - --color-infinity: #B67F33; + --color-infinity: #b67f33; --color-eternity: #b341e0; --color-dilation: #64dd17; --color-reality: #0ba00e; --color-reality-light: #d5ffd7; + --color-reality-dark: #0a790c; --color-celestials: #5151ec; --color-prestige--accent: white; @@ -50,49 +77,52 @@ html { --color-effarig--base: #d13737; - --color-enslaved-base: #f1aa7f; + --color-enslaved--base: #f1aa7f; --color-v--base: #ead584; - --color-ra-base: #9575cd; - --color-ra-pet-teresa: #8596ea; - --color-ra-pet-effarig: #ea8585; - --color-ra-pet-enslaved: #f1aa7f; - --color-ra-pet-v: #ead584; + --color-ra--base: #9575cd; + --color-ra-pet--teresa: #8596ea; + --color-ra-pet--effarig: #ea8585; + --color-ra-pet--enslaved: #f1aa7f; + --color-ra-pet--v: #ead584; --color-laitela--base: white; --color-laitela--accent: black; --color-pelle--base: crimson; - --color-pelle-secondary: #00bcd4; + --color-pelle--secondary: #00bcd4; } -.t-metro #ui-container, /* csslint allow: empty-rules */ -.t-inverted-metro #ui-container, -.t-s8 #ui-container { - --color-text: black; +:root .t-metro, +:root .t-inverted-metro, +:root .t-s8 { + --color-text: #000000; --color-text-inverted: white; --color-base: #eeeeee; --color-disabled: #9e9e9e; - --color-accent: #2196F3; + --color-accent: #2196f3; --color-good: #66bb6a; --color-good-dark: #388e3c; --color-bad: #ef5350; - --color-infinity: #FF9800; - --color-eternity: #673AB7; - --color-reality: #4caf50; + --color-infinity: #ff9800; + --color-eternity: #673ab7; + --color-reality: #3a823d; + --color-reality-light: #d5ffd7; --color-celestials: #00bcd4; } -.t-dark #ui-container { /* csslint allow: empty-rules */ +:root .t-dark { --color-text: #e0e0e0; --color-text-inverted: black; --color-base: #455a64; --color-disabled: #37474f; - --color-accent: #1565C0; + --color-accent: #1565c0; + --color-notification: yellow; - --color-infinity: #FF9800; + --color-infinity: #ff9800; + --color-reality-dark: #0ba00e; --color-prestige--accent: black; --color-teresa--accent: black; @@ -101,19 +131,21 @@ html { --color-laitela--accent: white; } -.t-dark-metro #ui-container { /* csslint allow: empty-rules */ +:root .t-dark-metro { --color-text: #e0e0e0; --color-text-inverted: black; --color-base: #455a64; --color-disabled: #37474f; - --color-accent: #2196F3; + --color-accent: #2196f3; --color-good: #43a047; --color-good-dark: #2e7d32; --color-bad: #e53935; + --color-notification: yellow; - --color-infinity: #FF9800; - --color-eternity: #673AB7; + --color-infinity: #ff9800; + --color-eternity: #673ab7; --color-reality: #4caf50; + --color-reality-dark: #0ba00e; --color-celestials: #00bcd4; --color-prestige--accent: black; @@ -123,8 +155,8 @@ html { --color-laitela--accent: white; } -.t-s1 #ui-container { /* csslint allow: empty-rules */ - --color-text: black; +:root .t-s1 { + --color-text: #000000; --color-text-inverted: #dbd242; --color-base: #dbd242; --color-disabled: #9a921d; @@ -135,14 +167,17 @@ html { --color-infinity: #7d3c1b; --color-eternity: #639565; - --color-reality: #AFA3A5; + --color-reality: #afa3a5; --color-reality-light: #e8e3e4; - --color-celestials: #F2D6C1; - --color-prestige--accent: black; + --color-reality-dark: #7a7273; + --color-celestials: #f2d6c1; + --color-prestige--accent: #dbd242; + + --color-pelle--base: #7cb727; } -.t-s4 #ui-container { /* csslint allow: empty-rules */ - --color-text: black; +:root .t-s4 { + --color-text: #000000; --color-text-inverted: white; --color-base: #1b00ff; --color-accent: #1b00ff; @@ -150,12 +185,15 @@ html { --color-bad: #ff0000; } -.t-s6 #ui-container, /* csslint allow: empty-rules */ -.t-s10 #ui-container { - --color-text: #E0E0E0; +:root .t-s6, +:root .t-s10 { + --color-text: #e0e0e0; --color-text-inverted: black; --color-base: black; - --color-accent: #1565C0; + --color-accent: #1565c0; + --color-good: #2f9e35; + --color-good-dark: #08510b; + --color-notification: yellow; --color-prestige--accent: black; @@ -165,11 +203,12 @@ html { --color-laitela--accent: white; } -.t-s11 #ui-container { /* csslint allow: empty-rules */ - --color-text: #E0E0E0; +:root .t-s11 { + --color-text: #e0e0e0; --color-text-inverted: black; --color-base: black; --color-accent: #fbc21b; + --color-notification: yellow; --color-prestige--accent: black; @@ -179,68 +218,90 @@ html { --color-laitela--accent: white; } +:root .s-base--metro { + --var-border-width: 0.1rem; + --var-border-radius: 0; +} + /* end css vars */ body { - height: 100%; + overflow: hidden; width: 100%; + height: 100%; + text-align: center; + font-family: Typewriter, serif; + font-size: 1.1rem; + font-weight: 300; + line-height: 1.5; + color: #4f5957; + background-color: #ffffff; margin: 0; padding: 0; - color:#4F5957; - font-size: 1.1rem; - font-family: Typewriter, serif; - line-height: 1.5; - font-weight: 300; - text-align:center; - background-color: #fff; - overflow: hidden; } ._kong-test body { + overflow-y: auto; width: 1150px; height: 800px; - margin: auto; border: 0.1rem solid black; - overflow-y: auto; + margin: auto; } @font-face { - font-family: 'Open Sans Condensed'; + font-family: "Open Sans Condensed"; font-style: normal; font-weight: 300; - src: local('Open Sans Cond Light'), local('OpenSans-CondensedLight'), url(https://fonts.gstatic.com/s/opensanscondensed/v10/gk5FxslNkTTHtojXrkp-xBEur64QvLD-0IbiAdTUNXE.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; + src: + local("Open Sans Cond Light"), + local("OpenSans-CondensedLight"), + url("https://fonts.gstatic.com/s/opensanscondensed/v10/gk5FxslNkTTHtojXrkp-xBEur64QvLD-0IbiAdTUNXE.woff2") format("woff2"); + unicode-range: + U+0000-00FF, + U+0131, + U+0152-0153, + U+02C6, + U+02DA, + U+02DC, + U+2000-206F, + U+2074, + U+20AC, + U+2212, + U+2215, + U+E0FF, + U+EFFD, + U+F000; } @font-face { font-family: Typewriter; - src: url('MonospaceTypewriter.ttf'); + src: url("MonospaceTypewriter.ttf"); } @font-face { - font-family: Noto Sans; - src: url('MonospaceTypewriter.ttf'); - unicode-range: U+F420-F430, U+2BC5-2BC6; + font-family: "Noto Sans"; + src: url("MonospaceTypewriter.ttf"); + unicode-range: U+F420-F430, U+2BC5-2BC6; } @font-face { font-family: Runescape; - src: url('Runescape.ttf'); + src: url("Runescape.ttf"); } @font-face { font-family: Barrio; - src: url('Barrio-Regular.ttf'); + src: url("Barrio-Regular.ttf"); } @font-face { - font-family: Font Awesome; - src: url('fa-solid-900.ttf'); + font-family: "Font Awesome"; + src: url("fa-solid-900.ttf"); } @font-face { font-family: Typewriter; - src: url('BlobEmoji-Bold.ttf'), url('MonospaceTypewriter.ttf'); + src: url("BlobEmoji-Bold.ttf"), url("MonospaceTypewriter.ttf"); } /* Background for s6 and s10 is not displayed by default */ @@ -252,17 +313,110 @@ button:focus { outline: none; } -/*#region TT shop*/ +/* #region CustomizeableTooltip */ -.TTbuttons { - color: var(--color-text); - position: fixed; - bottom: 0; - font-family: Typewriter, serif; +.c-tooltip-content, +.c-tooltip-arrow { + visibility: hidden; + opacity: 0; + transition: 0.4s linear; + transition-property: opacity, visibility; + pointer-events: none; +} + +.c-tooltip-content { + content: attr(ach-tooltip); + width: 16rem; + position: absolute; + z-index: 4; + text-align: center; + font-size: 1.4rem; + line-height: 1.2; + color: #ffffff; + background-color: #0d0d0d; + border-radius: var(--var-border-radius, 0.3rem); + padding: 0.7rem; +} + +.c-tooltip-content--dark { + border: 0.1rem solid #dddddd; +} + +.c-tooltip-arrow { + content: " "; + width: 0; + position: absolute; + z-index: 4; + font-size: 0; + line-height: 0; + border: 0.55rem solid #0d0d0d; + transform: translate(-50%, -100%); + transition-duration: 0.4s; +} + +.c-tooltip-arrow--dark { + border-color: #dddddd; +} + +.c-tooltip--top.c-tooltip-content { + margin-top: -0.5rem; +} + +.c-tooltip--top.c-tooltip-arrow { + border-right-color: transparent; + border-bottom: 0; + border-left-color: transparent; +} + +.c-tooltip--bottom.c-tooltip-content { + margin-bottom: -0.5rem; +} + +.c-tooltip--bottom.c-tooltip-arrow { + border-top: 0; + border-right-color: transparent; + border-left-color: transparent; +} + +.c-tooltip--right.c-tooltip-content { + margin-right: -0.5rem; +} + +.c-tooltip--right.c-tooltip-arrow { + border-top-color: transparent; + border-bottom-color: transparent; + border-left: 0; +} + +.c-tooltip--left.c-tooltip-content { + margin-left: -0.5rem; +} + +.c-tooltip--left.c-tooltip-arrow { + border-top-color: transparent; + border-right: 0; + border-bottom-color: transparent; +} + +.c-tooltip-show { + visibility: visible; + opacity: 1; +} + +/* #endregion CustomizeableTooltip */ + +/* #region TT shop */ + +.time-theorem-buttons { display: flex; - align-items: flex-end; - z-index: 2; width: 55.5rem; + position: absolute; + bottom: 0; + z-index: 2; + align-items: flex-end; + font-family: Typewriter, serif; + color: var(--color-text); + pointer-events: all; } .l-tt-buy-button { @@ -272,42 +426,38 @@ button:focus { } .c-tt-buy-button { - font-weight: bold; font-family: Typewriter, serif; - border: 0.1rem solid #691fa5; - transition-duration: 0.2s; - border-radius: 4px; font-size: 1.35rem; -} - -.s-base--metro .c-tt-buy-button{ - border-radius: 0; + font-weight: bold; + border: 0.1rem solid #691fa5; + border-radius: var(--var-border-radius, 4px); + transition-duration: 0.2s; } .c-tt-buy-button--unlocked { - color: #3AACD6; + color: #3aacd6; background: #232028; cursor: pointer; } .c-tt-buy-button--unlocked:hover { color: #232028; - background: #3AACD6; + background: #3aacd6; } .c-tt-buy-button--locked { color: black; - background: #4A4A4A; + background: #4a4a4a; } .c-tt-buy-button--locked:hover { - background: #652F2F; + background: #652f2f; } .timetheorems { - font-size: 15px; - text-align: center; flex: 1 0 auto; + text-align: center; + font-size: 15px; } .ttbuttons-row { @@ -315,6 +465,7 @@ button:focus { flex-direction: row; justify-content: space-between; align-items: stretch; + transition: all 0.25s ease-out; } .ttbuttons-top-row { @@ -322,53 +473,53 @@ button:focus { } .o-tt-top-row-button { - min-height: 3rem; - font-size: 1.2rem; - margin: 0.3rem; flex-grow: 0; flex-shrink: 0; + min-height: 3rem; align-self: stretch; - padding-left: 1rem; + font-size: 1.2rem; + margin: 0.3rem; padding-right: 1rem; + padding-left: 1rem; } .o-tt-autobuyer-button { - height: 2.5rem; - font-size: 1rem !important; - margin: 0.3rem; flex-grow: 0; flex-shrink: 0; - align-self: stretch; - padding-left: 1rem; - padding-right: 1rem; + height: 2.5rem; min-width: 8rem; + align-self: stretch; + font-size: 1rem !important; + margin: 0.3rem; + padding-right: 1rem; + padding-left: 1rem; } .l-tt-save-load-btn__wrapper { - margin: 0.3em; position: relative; + margin: 0.3rem; } .l-tt-save-load-btn { - min-width: 2em; + min-width: 2rem; } .l-tt-save-load-btn__menu { position: absolute; top: -0.5rem; left: 50%; - transform: translate(-50%, -100%); padding: 0.5rem 0; + transform: translate(-50%, -100%); } .c-tt-save-load-btn__menu { - color: white; - background: black; - border-radius: 0.5rem; text-align: left; - font-weight: bold; font-family: Typewriter; font-size: 1.4rem; + font-weight: bold; + color: white; + background: black; + border-radius: var(--var-border-radius, 0.5rem); } .l-tt-save-load-btn__menu::after { @@ -376,10 +527,10 @@ button:focus { position: absolute; top: 100%; left: 50%; - margin-left: -0.5rem; - border-width: 0.5rem; - border-style: solid; border-color: black transparent transparent; + border-style: solid; + border-width: var(--var-border-width, 0.5rem); + margin-left: -0.5rem; } .l-tt-save-load-btn__menu-rename { @@ -388,11 +539,11 @@ button:focus { .c-tt-save-load-btn__menu-rename { text-align: left; - font-weight: bold; font-family: Typewriter; font-size: 1.4rem; + font-weight: bold; border: none; - border-radius: 0.3rem; + border-radius: var(--var-border-radius, 0.3rem); padding: 0.2rem; } @@ -402,8 +553,8 @@ button:focus { } .c-tt-save-load-btn__menu-item:hover { - background: white; color: black; + background: white; } .c-tt-autobuyer-toggle { @@ -417,100 +568,92 @@ button:focus { } .ttshop-background { - background: var(--color-base); - border: 2px solid black; - border-bottom: none; - border-top-left-radius: 7px; - border-top-right-radius: 7px; z-index: 1; -} - -.s-base--metro .ttshop-background { - border-width: 1px; - border-radius: 0; + background: var(--color-base); + border: var(--var-border-width, 0.2rem) solid black; + border-bottom: none; + border-top-left-radius: var(--var-border-radius, 7px); + border-top-right-radius: var(--var-border-radius, 7px); } .t-s6 .ttshop-background, .t-s10 .ttshop-background { - border: 0.1rem solid #3AACD6; + border: 0.1rem solid #3aacd6; } .ttshop-container { - padding: 0 0.5rem; - min-width: 60rem; - max-width: 80rem; display: flex; flex-direction: column; + min-width: 60rem; + max-width: 80rem; + padding: 0 0.5rem; transition: all 0.25s ease-out; } .ttshop-minimize-btn { display: flex; - justify-content: center; - align-items: center; - color: var(--color-text); - font-size: 2.1rem; width: 2.5rem; height: 2.5rem; - margin-left: -0.2rem + justify-content: center; + align-items: center; + font-size: 2.1rem; + color: var(--color-text); + margin-left: -0.2rem; + cursor: pointer; } .t-s6 .ttshop-minimize-btn, .t-s10 .ttshop-minimize-btn, .s-base--metro .ttshop-minimize-btn { - margin-left: -0.1rem + margin-left: -0.1rem; } .t-s6 .ttshop-minimize-btn, .t-s10 .ttshop-minimize-btn { - color: #3AACD6; -} - -.ttshop-minimize-btn span { - transition: all 0.25s ease-out; + color: #3aacd6; } .c-ttshop__save-load-text { font-size: 10px; } -/*#endregion TT shop*/ +/* #endregion TT shop */ .l-dim-path-priority { position: absolute; right: 0.5rem; bottom: 0.5rem; - animation: weee 4s infinite; } .o-time-study-selection-btn { - border: 0.1rem solid; - background-color: black; - font-weight: bold; - font-family: Typewriter, serif; width: 20rem; height: 6rem; + position: relative; + font-family: Typewriter, serif; + font-size: 1.65rem; + font-weight: bold; + background-color: black; + border: 0.1rem solid; + border-radius: var(--var-border-radius, 0.4rem); margin: 1rem; transition-duration: 0.2s; - border-radius: 0.4rem; - font-size: 1.65rem; - position:relative; } -/*#region tab buttons*/ +/* #region tab buttons */ .o-tab-btn { - color: var(--color-text); - background-color: var(--color-base); white-space: nowrap; - font-weight: bold; - border: 0.2rem solid var(--color-accent); - border-radius: 0.4rem; - transition-duration: 0.2s; - cursor: pointer; font-family: Typewriter; font-size: 1.8rem; + font-weight: bold; + color: var(--color-text); + background-color: var(--color-base); + border: var(--var-border-width, 0.2rem) solid var(--color-accent); + border-radius: var(--var-border-radius, 0.4rem); + transition-duration: 0.2s; + -webkit-user-select: none; user-select: none; + cursor: pointer; } .t-s6 .o-tab-btn, @@ -519,83 +662,90 @@ button:focus { } .o-tab-btn:hover { - color: var(--color-text-inverted); + color: var(--color-prestige--accent); background: var(--color-accent); } +.o-tab-btn--modern-tabs:last-child { + border-bottom-right-radius: var(--var-border-radius, 0.5rem); +} + +.o-tab-btn--modern-tabs:last-child:hover { + border-bottom-right-radius: 0; +} + .o-tab-btn--infinity { color: var(--color-infinity); - background: var(--color-prestige--accent); + background-color: var(--color-prestige--accent); border-color: var(--color-infinity); } .o-tab-btn--infinity:hover { - color: var(--color-prestige--accent); background: var(--color-infinity); } .o-tab-btn--eternity { color: var(--color-eternity); - background: var(--color-prestige--accent); + background-color: var(--color-prestige--accent); border-color: var(--color-eternity); } .o-tab-btn--eternity:hover { - color: var(--color-prestige--accent); background: var(--color-eternity); } .o-tab-btn--reality { color: var(--color-reality); - background: var(--color-prestige--accent); + background-color: var(--color-prestige--accent); border-color: var(--color-reality); } .o-tab-btn--reality:hover { - color: var(--color-prestige--accent); background: var(--color-reality); } .o-tab-btn--celestial { color: var(--color-celestials); - background: var(--color-prestige--accent); + background-color: var(--color-prestige--accent); border-color: var(--color-celestials); } .o-tab-btn--celestial:hover { - color: var(--color-prestige--accent); background: var(--color-celestials); } -/*#endregion tab buttons*/ +/* #endregion tab buttons */ #loading { - height: 100%; width: 100%; + height: 100%; + position: absolute; + z-index: 999; background: url("../images/loading.png") no-repeat; - background-size: cover; + background-color: black; background-position-x: 50%; background-position-y: 50%; - position: absolute; - background-color: black; - z-index: 999; + background-size: cover; } #browser-warning { - height: 100%; - width: 100%; - z-index: 9999; - align-items: center; - justify-content: center; - background-color: black; - font-size: 7rem; display: none; + width: 100%; + height: 100%; + z-index: 9999; + justify-content: center; + align-items: center; + font-size: 7rem; + background-color: black; } .l-reality-button { - display: block; - width: 50%; - height: 100%; + width: 35rem; + height: 6rem; +} + +.s-base--metro .l-reality-button { + box-shadow: 0.1rem 0.1rem 0.1rem 0 #9e9e9e; } .l-reality-button__contents { @@ -605,21 +755,21 @@ button:focus { } .c-reality-button__header { - font-size: 1.4rem; + font-size: 1.2rem; line-height: 1.6; } .c-reality-button { - font-family: Typewriter; - border-radius: 0.4rem; - transition-duration: 0.2s; width: 100%; height: 100%; + font-family: Typewriter; font-size: 1.2rem; font-weight: bold; - background: black; - border-width: 0.2rem; + background: var(--color-background); border-style: solid; + border-width: var(--var-border-width, 0.2rem); + border-radius: var(--var-border-radius, 0.4rem); + transition-duration: 0.2s; } .c-reality-button--special { @@ -628,24 +778,19 @@ button:focus { .c-reality-reminder { display: flex; + width: calc(100% - 1rem); justify-content: center; align-items: center; font-size: 1.4rem; border: 0.1rem solid #b8b8b8; - border-radius: 0.5rem; - width: calc(100% - 1rem); + border-radius: var(--var-border-radius, 0.5rem); padding: 1rem; } @keyframes a-celstial-reward-glow { - 0% { box-shadow: inset 0 0 4rem var(--color-celestials) } - 50% { box-shadow: inset 0 0 0 } - 100% { box-shadow: inset 0 0 4rem var(--color-celestials) } -} - -.s-base--metro .c-reality-button { - border-width: 0.1rem; - border-radius: 0; + 0% { box-shadow: inset 0 0 4rem var(--color-celestials); } + 50% { box-shadow: inset 0 0 0; } + 100% { box-shadow: inset 0 0 4rem var(--color-celestials); } } .t-s6 .c-reality-button, @@ -654,14 +799,14 @@ button:focus { } .c-reality-button--unlocked { - color: var(--color-reality); - border-color: var(--color-reality); + color: var(--color-reality-dark); + border-color: var(--color-reality-dark); cursor: pointer; } .c-reality-button--locked { color: #181818; - background-color: #5f5f5f; + background-color: var(--color-glyph-sac-text-input--focused); border-color: var(--color-bad); } @@ -670,7 +815,8 @@ button:focus { color: grey; } -.c-reality-button--unlocked:hover, .c-reality-button--unlocked.force-hover { +.c-reality-button--unlocked:hover, +.c-reality-button--unlocked.force-hover { color: black; background: var(--color-reality); } @@ -683,145 +829,153 @@ button:focus { .l-reset-reality-button { display: block; width: 100%; + height: 100%; } .c-reset-reality-button { font-family: Typewriter; - border-radius: 0.4rem; - transition-duration: 0.2s; - cursor: pointer; - height: 35%; font-size: 1.2rem; font-weight: bold; color: var(--color-bad); + background: var(--color-background); border-color: var(--color-bad); - background: white; - border-width: 0.2rem; border-style: solid; + border-width: var(--var-border-width, 0.2rem); + border-radius: var(--var-border-radius, 0.4rem); + transition-duration: 0.2s; + cursor: pointer; } -.c-reset-reality-button:hover, .c-reset-reality-button.force-hover, -.t-s6 .c-reset-reality-button:hover, .t-s6 .c-reset-reality-button.force-hover, -.t-s10 .c-reset-reality-button:hover, .t-s10 .c-reset-reality-button.force-hover { - color: white; +.c-reset-reality-button:hover, +.c-reset-reality-button.force-hover, +.t-s6 .c-reset-reality-button:hover, +.t-s6 .c-reset-reality-button.force-hover, +.t-s10 .c-reset-reality-button:hover, +.t-s10 .c-reset-reality-button.force-hover { + color: black; background: var(--color-bad); } -.s-base--metro .c-reset-reality-button { - border-width: 0.1rem; - border-radius: 0; -} - -.t-s6 .c-reset-reality-button, -.t-s10 .c-reset-reality-button { - background: black; -} - .c-reset-reality-button-celestial { color: var(--color-celestials); border-color: var(--color-celestials); } -.c-reset-reality-button-celestial:hover, .c-reset-reality-button-celestial.force-hover, -.t-s6 .c-reset-reality-button-celestial:hover, .t-s6 .c-reset-reality-button-celestial.force-hover, -.t-s10 .c-reset-reality-button-celestial:hover, .t-s10 .c-reset-reality-button-celestial.force-hover { - color: white; +.c-reset-reality-button-celestial:hover, +.c-reset-reality-button-celestial.force-hover, +.t-s6 .c-reset-reality-button-celestial:hover, +.t-s6 .c-reset-reality-button-celestial.force-hover, +.t-s10 .c-reset-reality-button-celestial:hover, +.t-s10 .c-reset-reality-button-celestial.force-hover { + color: black; background: var(--color-celestials); } -.s-base--metro .c-reset-reality-button-celestial { - border-width: 0.1rem; - border-radius: 0; -} - -.t-s6 .c-reset-reality-button-celestial, -.t-s10 .c-reset-reality-button-celestial { - background: black; -} - .l-reality-amplify-button { - border-radius: 0.5rem; - border: 0.2rem solid sandybrown; - box-shadow: 0 0 2rem inset rgba(244, 164, 96, 0.5); - background-color: #fdd3b0; - cursor: pointer; font-family: Typewriter; + background-color: #fdd3b0; + border: var(--var-border-width, 0.2rem) solid sandybrown; + border-radius: var(--var-border-radius, 0.5rem); + box-shadow: 0 0 2rem inset rgba(244, 164, 96, 50%); + margin-right: 0.5rem; transition-duration: 0.2s; } +.l-reality-amplify-button--clickable { + cursor: pointer; +} + [draggable] { - -moz-user-select: none; - -khtml-user-select: none; -webkit-user-select: none; - -ms-user-select: none; user-select: none; + /* Required to make elements draggable in old WebKit */ -khtml-user-drag: element; -webkit-user-drag: element; } @keyframes a-existence-glow { - 0% { color: var(--color-text-inverted); text-shadow: 0.1rem 0.1rem 0.2rem var(--color-text); } - 50% { color: var(--color-text-inverted); text-shadow: 0.1rem 0.1rem 0.8rem var(--color-text); } - 100% { color: var(--color-text-inverted); text-shadow: 0.1rem 0.1rem 0.2rem var(--color-text); } + 0% { + color: var(--color-text-inverted); + text-shadow: 0.1rem 0.1rem 0.2rem var(--color-text); + } + + 50% { + color: var(--color-text-inverted); + text-shadow: 0.1rem 0.1rem 0.8rem var(--color-text); + } + + 100% { + color: var(--color-text-inverted); + text-shadow: 0.1rem 0.1rem 0.2rem var(--color-text); + } } [ach-tooltip] { position: relative; } -[ach-tooltip]:before, -[ach-tooltip]:after { +[ach-tooltip]::before, +[ach-tooltip]::after { visibility: hidden; opacity: 0; pointer-events: none; } -[ach-tooltip]:before { +[ach-tooltip]::before { + content: attr(ach-tooltip); + width: 16rem; position: absolute; bottom: 100%; left: 50%; - margin-bottom: 0.5rem; - transform: translateX(-50%); - padding: 0.7rem; - width: 16rem; - border-radius: 0.3rem; - background-color: hsla(0, 0%, 5%, 0.9); - color: #fff; - content: attr(ach-tooltip); + z-index: 3; text-align: center; font-size: 1.4rem; line-height: 1.2; + color: #ffffff; + background-color: #0d0d0d; + border-radius: var(--var-border-radius, 0.3rem); + margin-bottom: 0.5rem; + padding: 0.7rem; + transform: translateX(-50%); transition-duration: 0.4s; - z-index: 3; } -.t-dark-metro [ach-tooltip]:before { - border-radius: 0; +.s-base--dark [ach-tooltip]::before { + border: 0.1rem solid #dddddd; } -[ach-tooltip]:after { +[ach-tooltip]::after { + content: " "; + width: 0; position: absolute; bottom: 100%; left: 50%; - margin-left: -0.5rem; - width: 0; - border-top: 0.5rem solid hsla(0, 0%, 5%, 0.9); - border-right: 0.5rem solid transparent; - border-left: 0.5rem solid transparent; - content: " "; font-size: 0; line-height: 0; + border-top: 0.5rem solid #0d0d0d; + border-right: 0.5rem solid transparent; + border-left: 0.5rem solid transparent; + margin-left: -0.5rem; transition-duration: 0.4s; } -[ach-tooltip]:hover:before, -[ach-tooltip]:hover:after { +.s-base--dark [ach-tooltip]::after { + border-top: 0.5rem solid #dddddd; +} + +[ach-tooltip]:hover::before, +[ach-tooltip]:hover::after { visibility: visible; opacity: 1; } -h1, h2, h3, h4, h5, h6 { +h1, +h2, +h3, +h4, +h5, +h6 { margin: 0 0 1rem; } @@ -833,7 +987,9 @@ dl { margin: 0 0 3px; } -h1, h2, h3 { +h1, +h2, +h3 { line-height: 1.1; } @@ -842,23 +998,23 @@ h1 { } a { - color: #39c; + color: #3399cc; text-decoration: none; } a:hover { - color: #069; + color: #006699; } a small { - font-size: 11px; - color: #777; - margin-top: -0.3em; display: block; + font-size: 11px; + color: #777777; + margin-top: -0.3rem; } a:hover small { - color: #777; + color: #777777; } .wrapper { @@ -867,14 +1023,14 @@ a:hover small { } blockquote { + font-style: italic; border-left: 0.1rem solid #e5e5e5; margin: 0; padding: 0 0 0 20px; - font-style: italic; } table { - border-spacing: 0 + border-spacing: 0; } th, @@ -884,12 +1040,12 @@ td { } dt { - color: #444; font-weight: 700; + color: #444444; } th { - color: #444; + color: #444444; } img { @@ -897,27 +1053,27 @@ img { } header { - width: 270px; float: left; + width: 270px; position: fixed; -webkit-font-smoothing: subpixel-antialiased; } header ul { list-style: none; - height: 40px; - padding: 0; - background: #f4f4f4; - border-radius: 5px; - border: 0.1rem solid #e0e0e0; width: 270px; + height: 40px; + background: #f4f4f4; + border: 0.1rem solid #e0e0e0; + border-radius: var(--var-border-radius, 5px); + padding: 0; } header li { - width: 89px; float: left; - border-right: 0.1rem solid #e0e0e0; + width: 89px; height: 40px; + border-right: 0.1rem solid #e0e0e0; } header li:first-child a { @@ -929,17 +1085,17 @@ header li:last-child a { } header ul a { - line-height: 1; - font-size: 11px; - color: #999; display: block; - text-align: center; - padding-top: 6px; height: 34px; + text-align: center; + font-size: 11px; + line-height: 1; + color: #999999; + padding-top: 6px; } header ul a:hover { - color: #999; + color: #999999; } header ul a:active { @@ -947,23 +1103,23 @@ header ul a:active { } strong { -font-weight: 700; + font-weight: 700; } -header ul li+li+li { - border-right: none; +header ul li + li + li { width: 89px; + border-right: none; } header ul a strong { - font-size: 14px; display: block; - color: #222; + font-size: 14px; + color: #222222; } section { - width: 500px; float: right; + width: 500px; padding-bottom: 50px; } @@ -972,15 +1128,15 @@ small { } hr { - border: 0; - background: #e5e5e5; height: 1px; + background: #e5e5e5; + border: 0; margin: 0 0 20px; } footer { - width: 270px; float: left; + width: 270px; position: fixed; bottom: 50px; -webkit-font-smoothing: subpixel-antialiased; @@ -988,17 +1144,17 @@ footer { br { margin: 0; - pointer-events:none; + pointer-events: none; } .videocontainer { + overflow: hidden; + width: 100%; + height: 100%; position: fixed; top: 0; bottom: 0; - width: 100%; - height: 100%; z-index: 1; - overflow: hidden; pointer-events: none; } @@ -1007,15 +1163,15 @@ br { } #realityanimbg { - min-width: 100%; - min-height: 100%; width: auto; height: auto; + min-width: 100%; + min-height: 100%; position: absolute; - pointer-events: all; top: 50%; left: 50%; - transform: translate(-50%,-50%); + transform: translate(-50%, -50%); + pointer-events: all; } @media print, screen and (max-width: 960px) { @@ -1023,40 +1179,47 @@ br { width: auto; margin: 0; } + header, section, footer { float: none; - position: static; width: auto; + position: static; } + header { padding-right: 320px; } + section { border: 0.1rem solid #e5e5e5; - border-width: 1px 0; - padding: 20px 0; + border-width: var(--var-border-width, 1px) var(--var-border-width, 0); margin: 0 0 20px; + padding: 20px 0; } + header a small { display: inline; } + header ul { position: absolute; - right: 50px; top: 52px; + right: 50px; } } @media print, -screen and (max-width: 720px) { + screen and (max-width: 720px) { body { word-wrap: break-word; } + header { padding: 0; } + header ul, header p.view { position: static; @@ -1064,156 +1227,261 @@ screen and (max-width: 720px) { } @media print, -screen and (max-width: 480px) { + screen and (max-width: 480px) { body { padding: 15px; } + header ul { width: 99%; } + header li, - header ul li+li+li { + header ul li + li + li { width: 33%; } } @media print { body { - padding: 0.4in; + /* stylelint-disable-next-line unit-allowed-list */ font-size: 12pt; - color: #444; + color: #444444; + /* stylelint-disable-next-line unit-allowed-list */ + padding: 0.4in; } } -@keyframes barrelRoll { - 0% {transform: rotateZ(0deg);} +@keyframes a-barrel-roll { + 0% { transform: rotateZ(0deg); } + 50%, - 100% {transform: rotateZ(360deg);} + 100% { transform: rotateZ(360deg); } } -@keyframes spin3d { - 0% {transform: rotate3d(5.2, -2.8, 1.4, 0deg);} - 100% {transform: rotate3d(5.2, -2.8, 1.4, 360deg);} +@keyframes a-spin3d { + 0% { transform: rotate3d(5.2, -2.8, 1.4, 0deg); } + 100% { transform: rotate3d(5.2, -2.8, 1.4, 360deg); } } -@keyframes spin4d { +@keyframes a-spin4d { 0%, - 100% {transform: scale(1) rotate3d(5.2, -2.8, 1.4, 0deg);} - 50% {transform: scale(0) rotate3d(5.2, -2.8, 1.4, 360deg);} + 100% { transform: scale(1) rotate3d(5.2, -2.8, 1.4, 0deg); } + 50% { transform: scale(0) rotate3d(5.2, -2.8, 1.4, 360deg); } } -@keyframes float { - 0% { bottom: 0; opacity: 0;} - 50% {opacity: 1; } - 100% { bottom: 50px; opacity: 0;} +@keyframes a-float { + 0% { + bottom: 0; + opacity: 0; + } + + 50% { + opacity: 1; + } + + 100% { + bottom: 50px; + opacity: 0; + } } -@keyframes implode { +@keyframes a-implode { 0%, - 100% {transform:scale(1); filter:blur(0)} - 50% {transform:scale(0); filter:blur(5px)} + 100% { + transform: scale(1); + filter: blur(0); + } + + 50% { + transform: scale(0); + filter: blur(5px); + } } -@keyframes eternify { - 0% {opacity: 1; filter: blur(0);} - 15% {opacity: 0.5; filter: blur(1px);} - 30% {opacity: 0.75; filter: blur(0.5px);} - 45% {opacity: 0.25; filter: blur(1.5px);} - 60% {opacity: 0.5; filter: blur(1px);} - 75% {opacity: 0; filter: blur(3px);} - 85% {opacity: 0; filter: blur(3px);} - 100% {opacity: 1; filter: blur(0);} +@keyframes a-eternify { + 0% { + opacity: 1; + filter: blur(0); + } + + 15% { + opacity: 0.5; + filter: blur(1px); + } + + 30% { + opacity: 0.75; + filter: blur(0.5px); + } + + 45% { + opacity: 0.25; + filter: blur(1.5px); + } + + 60% { + opacity: 0.5; + filter: blur(1px); + } + + 75% { + opacity: 0; + filter: blur(3px); + } + + 85% { + opacity: 0; + filter: blur(3px); + } + + 100% { + opacity: 1; + filter: blur(0); + } } -@keyframes dilate { - 0% {transform: scaleX(1); opacity: 1} - 50% {transform: scaleX(3); opacity: 0} - 60% {transform: scaleX(1); opacity: 0} - 100% {transform: scaleX(1); opacity: 1} +@keyframes a-dilate { + 0% { + opacity: 1; + transform: scaleX(1); + } + + 50% { + opacity: 0; + transform: scaleX(3); + } + + 60% { + opacity: 0; + transform: scaleX(1); + } + + 100% { + opacity: 1; + transform: scaleX(1); + } } -@keyframes undilate { - 0% {transform: scaleX(1); opacity: 1} - 50% {transform: scaleX(0.5); opacity: 0} - 60% {transform: scaleX(1); opacity: 0} - 100% {transform: scaleX(1); opacity: 1} +@keyframes a-undilate { + 0% { + opacity: 1; + transform: scaleX(1); + } + + 50% { + opacity: 0; + transform: scaleX(0.5); + } + + 60% { + opacity: 0; + transform: scaleX(1); + } + + 100% { + opacity: 1; + transform: scaleX(1); + } } -@keyframes realize { - 0% {opacity: 1;} - 20% {opacity: 0;} - 80% {opacity: 0;} - 100% {opacity: 1;} +@keyframes a-realize { + 0% { opacity: 1; } + 20% { opacity: 0; } + 80% { opacity: 0; } + 100% { opacity: 1; } } -@keyframes realizebg { - 0% {opacity: 0;} - 20% {opacity: 0;} - 30% {opacity: 1;} - 70% {opacity: 1;} - 80% {opacity: 0;} - 100% {opacity: 0;} +@keyframes a-realizebg { + 0% { opacity: 0; } + 20% { opacity: 0; } + 30% { opacity: 1; } + 70% { opacity: 1; } + 80% { opacity: 0; } + 100% { opacity: 0; } } -@keyframes text-grow { - 0% {font-size: 1.3rem;} - 50% {font-size: 1.8rem;} - 100% {font-size: 1.3rem;} +@keyframes a-text-grow { + 0% { font-size: 1.3rem; } + 50% { font-size: 1.8rem; } + 100% { font-size: 1.3rem; } } -@keyframes text-shrink { - 0% {font-size: 1.8rem;} - 50% {font-size: 1.3rem;} - 100% {font-size: 1.8rem;} +@keyframes a-text-shrink { + 0% { font-size: 1.8rem; } + 50% { font-size: 1.3rem; } + 100% { font-size: 1.8rem; } } -@keyframes text-crunch { - 0% {font-size: 1.5rem;} - 90% {font-size: 1.5rem;} - 100% {font-size: 0;} +@keyframes a-text-crunch { + 0% { font-size: 1.5rem; } + 90% { font-size: 1.5rem; } + 100% { font-size: 0; } } -@keyframes text-flash { - 0% {color: yellow;} - 50% {color: red;} +@keyframes a-text-flash { + 0% { color: yellow; } + 50% { color: red; } } -@keyframes fade-out { - 0% {opacity: 1;} - 50% {opacity: 0;} - 100% {opacity: 1;} +@keyframes a-fade-out { + 0% { opacity: 1; } + 50% { opacity: 0; } + 100% { opacity: 1; } } -@keyframes disappear { - 0% {opacity: 1;} - 100% {opacity: 0;} +@keyframes a-disappear { + 0% { opacity: 1; } + 100% { opacity: 0; } } -@keyframes text-stretch { - 0% {letter-spacing: 0;} - 100% {letter-spacing: 30rem;} +@keyframes a-text-stretch { + 0% { letter-spacing: 0; } + 100% { letter-spacing: 30rem; } } .infotooltip { - position: relative; display: inline-block; + position: relative; } .infotooltip .infotooltiptext { visibility: hidden; - background-color: black; - color: #fff; - text-align: left; - padding: 0.5rem; - border-radius: 0.5rem; - position: absolute; - z-index: 2; white-space: nowrap; - left: 105% + position: absolute; + left: 105%; + z-index: 2; + text-align: left; + opacity: 0; + color: var(--color-text); + background-color: var(--color-background); + border: 0.3rem solid var(--color-text); + border-radius: var(--var-border-radius, 0.5rem); + padding: 0.5rem; + transition: opacity 0.3s, visibility 0.3s; +} + +.s-base--dark .infotooltip .infotooltiptext { + border: 0.1rem solid #dddddd; } .infotooltip:hover .infotooltiptext { visibility: visible; + opacity: 1; +} + +.infotooltip .infotooltiptext::after { + content: ""; + position: absolute; + top: 50%; + right: 100%; + border: 0.5rem solid transparent; + border-right-color: #0d0d0d; + transform: translateY(-50%); +} + +.s-base--dark .infotooltip .infotooltiptext::after { + border-right-color: #dddddd; } /******* @@ -1221,28 +1489,41 @@ screen and (max-width: 480px) { *******/ .c-game-ui { + overflow-x: hidden; + overflow-y: auto; width: 100%; - height: 100%; + /* stylelint-disable-next-line unit-allowed-list */ + height: 100vh; + position: relative; +} + +.c-game-tab { + padding-bottom: 2rem; +} + +.c-wide-canvas-element { + width: 950px; + height: 600px; } ._kong-test .c-game-ui { max-height: 100%; } -/*#region o-primary-btn*/ +/* #region o-primary-btn */ .o-primary-btn { height: 2.5rem; font-family: Typewriter, serif; - font-weight: bold; font-size: 1.3rem; + font-weight: bold; color: var(--color-text); background-color: var(--color-base); border: 0.1rem solid var(--color-good-dark); - border-radius: 0.4rem; + border-radius: var(--var-border-radius, 0.4rem); + padding: 0.3rem 1rem; transition-duration: 0.2s; cursor: pointer; - padding: 0.3rem 1rem; } .o-primary-btn:hover { @@ -1251,7 +1532,7 @@ screen and (max-width: 480px) { } .o-primary-btn--disabled { - background-color: #A3A3A3; + background-color: #a3a3a3; border-color: var(--color-bad); cursor: default; } @@ -1260,103 +1541,112 @@ screen and (max-width: 480px) { background-color: var(--color-bad); } -/*#region modifiers*/ +/* #region modifiers */ .o-primary-btn--width-medium { width: 12rem; } .o-primary-btn--subtab-option { - border: 0.2rem solid var(--color-accent); - margin: 0.4rem 0.8rem; line-height: 0; + border: var(--var-border-width, 0.2rem) solid var(--color-accent); + margin: 0.4rem 0.8rem; } .o-primary-btn--subtab-option:hover { background-color: var(--color-accent); } -.o-primary-btn--tickspeed { - width: 17rem; - font-size: 1.2rem; - margin-right: 0.4rem; -} - .o-primary-btn--buy-max { - font-size: 1.2rem; width: 12rem; + font-size: 1.2rem; } .o-primary-btn--sacrifice { - font-size: 1.2rem; width: 48rem; + font-size: 1.2rem; margin-right: 0.8rem; } +.o-primary-btn--buy-dim { + max-width: 25rem; +} + .o-primary-btn--buy-ad { - font-size: 1rem; - vertical-align: middle; - line-height: 9px; + height: 4rem; + font-size: 1.2rem; } .o-primary-btn--buy-single-ad { - width: 13.5rem; + width: 40%; + margin: 0 0.5rem; } .o-primary-btn--buy-10-ad { - width: 21rem; + width: 60%; + max-width: 20rem; + margin: 0 0.5rem; +} + +.o-primary-btn--continuum-ad { + width: 100%; + max-width: 20rem; + margin: 0 0.5rem; + cursor: auto; } .o-primary-btn--dimboost { - font-size: 0.9rem; width: 21rem; height: 4.5rem; + font-size: 0.9rem; } .o-primary-btn--galaxy { - font-size: 0.9rem; width: 21rem; height: 4.5rem; + font-size: 0.9rem; } .o-primary-btn--quick-reset { - font-size: 1.2rem; width: 20rem; height: 7rem; + font-size: 1.2rem; } .o-primary-btn--buy-id { - width: 21rem; - height: 3rem; + width: 30rem; + height: 3.5rem; + margin: 0 0.5rem; } -.o-primary-btn--buy-id-max, -.o-primary-btn--id-autobuyer { +.o-primary-btn--id-auto { + width: 9rem; + height: 3.5rem; font-size: 1rem; - width: 8rem; - height: 3rem; + margin: 0 0.5rem; } .o-primary-btn--buy-td { - width: 21rem; - height: 3rem; + width: 30rem; + height: 3.5rem; + margin: 0 0.5rem; } -.o-primary-btn--buy-td-max, -.o-primary-btn--td-autobuyer { +.o-primary-btn--buy-td-auto { + width: 9rem; + height: 3.5rem; font-size: 1rem; - width: 8rem; - height: 3rem; + margin: 0 0.5rem; } .o-primary-btn--option { + display: flex; + flex-direction: column; width: 22rem; height: 5.5rem; - box-sizing: border-box; - display: flex; justify-content: center; align-items: center; - flex-direction: column; + box-sizing: border-box; } .o-primary-btn--option-wide { @@ -1378,7 +1668,12 @@ screen and (max-width: 480px) { } .o-primary-btn--slider__slider { - width: 100%; + height: 1.6rem; + margin: 0.2rem; +} + +.o-primary-btn--input { + cursor: default; } .o-primary-btn--slider { @@ -1470,11 +1765,11 @@ screen and (max-width: 480px) { font-size: 1rem; } -/*#endregion modifiers*/ +/* #endregion modifiers */ -/*#region themes*/ +/* #region themes */ -/*#region t-dark*/ +/* #region t-dark */ .t-dark .o-primary-btn { box-shadow: 0 0 0.7rem 0.2rem #111111; @@ -1501,14 +1796,12 @@ screen and (max-width: 480px) { background-color: #1b5e20; } -/*#endregion t-dark*/ +/* #endregion t-dark */ -/*#region t-dark-metro*/ +/* #region t-dark-metro */ .t-dark-metro .o-primary-btn { box-shadow: 0.1rem 0.1rem 0.1rem 0 black; - border-radius: 0; - border-width: 0.1rem; } .t-dark-metro .o-primary-btn--disabled { @@ -1519,18 +1812,9 @@ screen and (max-width: 480px) { background-color: var(--color-bad); } -/*#endregion t-dark-metro*/ +/* #endregion t-dark-metro */ -/*#region t-metro t-dark-metro t-inverted-metro t-s8*/ - -.s-base--metro .o-primary-btn { - border-radius: 0; - border-width: 0.1rem; -} - -.s-base--metro .o-primary-btn--toggle { - border-width: 0.1rem; -} +/* #region t-metro t-dark-metro t-inverted-metro t-s8 */ .t-metro .o-primary-btn, .t-inverted-metro .o-primary-btn { @@ -1547,9 +1831,9 @@ screen and (max-width: 480px) { background-color: var(--color-bad); } -/*#endregion t-metro t-inverted-metro t-s8*/ +/* #endregion t-metro t-inverted-metro t-s8 */ -/*#region t-s1*/ +/* #region t-s1 */ .t-s1 .o-primary-btn { border-color: black; @@ -1563,22 +1847,22 @@ screen and (max-width: 480px) { background-color: var(--color-bad); } -/*#endregion t-s1*/ +/* #endregion t-s1 */ -/*#region t-s4*/ +/* #region t-s4 */ .t-s4 .o-primary-btn { background-color: #ff0000; - cursor: url(cursor2.cur), auto; + cursor: url("cursor2.cur"), auto; } .t-s4 .o-primary-btn--disabled { - cursor: url(cursor.cur), auto; + cursor: url("cursor.cur"), auto; } -/*#endregion t-s4*/ +/* #endregion t-s4 */ -/*#region t-s6 t-s10*/ +/* #region t-s6 t-s10 */ .t-s6 .o-primary-btn, .t-s10 .o-primary-btn { @@ -1587,7 +1871,7 @@ screen and (max-width: 480px) { .t-s6 .o-primary-btn--disabled, .t-s10 .o-primary-btn--disabled { - color: grey; + color: #bbbbbb; background-color: var(--color-base); } @@ -1619,8 +1903,8 @@ screen and (max-width: 480px) { .t-s1 .o-primary-btn--respec-active:hover, .t-s6 .o-primary-btn--respec-active:hover, .t-s10 .o-primary-btn--respec-active:hover { - background-color: #673ab7; color: black; + background-color: #673ab7; } .s-base--metro .o-primary-btn--discharge-active, @@ -1634,44 +1918,47 @@ screen and (max-width: 480px) { .t-s1 .o-primary-btn--discharge-active:hover, .t-s6 .o-primary-btn--discharge-active:hover, .t-s10 .o-primary-btn--discharge-active:hover { - background-color: #673ab7; color: black; + background-color: #673ab7; } -/*#endregion t-s6 t-s10*/ +/* #endregion t-s6 t-s10 */ -/*#endregion themes*/ +/* #endregion themes */ -/*#endregion o-primary-btn*/ +/* #endregion o-primary-btn */ -/*#region c-subtab-option-container*/ +/* #region c-subtab-option-container */ .c-subtab-option-container { display: flex; - flex-direction: row; - padding-bottom: 0.8rem; - flex-wrap: wrap; + flex-flow: row wrap; justify-content: center; + padding-bottom: 0.8rem; } -.s-base--metro .c-subtab-option-container { - border-width: 0.1rem; - border-radius: 0; +/* #endregion c-subtab-option-container */ + +/* #region o-prestige-button */ + +.c-prestige-button-container { + display: flex; + flex-direction: column; + height: 14rem; + justify-content: space-around; + align-items: center; + padding: 1rem; } -/*#endregion c-subtab-option-container*/ - -/*#region o-prestige-button*/ - .o-prestige-button { - border: 0.2rem solid; - border-radius: 0.4rem; - transition-duration: 0.2s; - cursor: pointer; width: 22rem; height: 7rem; - font-size: 1.1rem; font-family: Typewriter, serif; + font-size: 1.1rem; + border: var(--var-border-width, 0.2rem) solid; + border-radius: var(--var-border-radius, 0.4rem); + transition-duration: 0.2s; + cursor: pointer; } .o-prestige-button br { @@ -1682,13 +1969,13 @@ screen and (max-width: 480px) { pointer-events: none; } -/*#region Big Crunch*/ +/* #region Big Crunch */ .o-infinity-button { - color: var(--color-infinity); - background-color: black; - border-color: var(--color-infinity); font-weight: bold; + color: var(--color-infinity); + background-color: var(--color-prestige--accent); + border-color: var(--color-infinity); } .o-infinity-button:hover { @@ -1705,33 +1992,31 @@ screen and (max-width: 480px) { } .s-base--metro .o-infinity-button { - border-width: 0.1rem; - border-radius: 0; box-shadow: 0.1rem 0.1rem 0.1rem 0 #9e9e9e; } .t-dark-metro .o-infinity-button { - box-shadow: 1px 1px 1px 0 black; + box-shadow: 0.1rem 0.1rem 0.1rem 0 #9e9e9e; } .o-infinity-button--unavailable { - cursor: default; opacity: 0.5; + cursor: default; } -/*#region Eternity*/ +/* #region Eternity */ .o-eternity-button { - color: var(--color-eternity); - background-color: black; - border-color: var(--color-eternity); font-weight: bold; + color: var(--color-eternity); + background-color: var(--color-prestige--accent); + border-color: var(--color-eternity); + cursor: pointer; } .o-eternity-button:hover { color: black; background-color: var(--color-eternity); - cursor: pointer; } .o-eternity-button span { @@ -1746,21 +2031,20 @@ screen and (max-width: 480px) { color: var(--color-text); } -.s-base--metro .o-eternity-button { - border-width: 0.1rem; - border-radius: 0; -} - .t-s1 .o-eternity-button:hover { color: black; border-color: black; } +.s-base--metro .o-eternity-button { + box-shadow: 0.1rem 0.1rem 0.1rem 0 #9e9e9e; +} + .o-eternity-button--dilation { + font-weight: bold; color: var(--color-dilation); background-color: black; border-color: var(--color-dilation); - font-weight: bold; } .o-eternity-button--dilation:hover { @@ -1780,11 +2064,6 @@ screen and (max-width: 480px) { color: var(--color-text); } -.s-base--metro .o-eternity-button--dilation { - border-width: 0.1rem; - border-radius: 0; -} - .t-s1 .o-eternity-button--dilation:hover { color: black; background: var(--color-dilation); @@ -1792,17 +2071,17 @@ screen and (max-width: 480px) { } .o-eternity-button--unavailable { - cursor: default; opacity: 0.5; + cursor: default; } -/*#endregion Eternity*/ +/* #endregion Eternity */ -/*#endregion Big Crunch*/ +/* #endregion Big Crunch */ -/*#endregion o-prestige-btn*/ +/* #endregion o-prestige-btn */ -/*#region l-spoon-btn-group*/ +/* #region l-spoon-btn-group */ .l-spoon-btn-group { display: flex; @@ -1821,14 +2100,14 @@ screen and (max-width: 480px) { margin-bottom: 1.2rem; } -/*#endregion l-spoon-btn-group*/ +/* #endregion l-spoon-btn-group */ .o-big-checkbox { width: 2rem; height: 1.8rem; } -/*#region hint-text*/ +/* #region hint-text */ .o-dim-path-priority { font-size: 1.55rem; @@ -1854,14 +2133,14 @@ screen and (max-width: 480px) { } .l-hint-text--reality-upgrade { - font-size: 1.25rem; top: -1.7rem; + font-size: 1.25rem; } .c-hint-text--reality-upgrade { + font-weight: bold; color: var(--color-text); text-shadow: none; - font-weight: bold; } .l-hint-text--achievement { @@ -1870,9 +2149,9 @@ screen and (max-width: 480px) { } .o-hint-text--alchemy-node { + font-size: 1.2rem; color: #4f5957; text-shadow: none; - font-size: 1.2rem; transition: opacity 0.2s; } @@ -1886,55 +2165,38 @@ screen and (max-width: 480px) { transform: translate(-50%); } -/*#endregion hint-text*/ +/* #endregion hint-text */ -/*#region Header*/ +/* #region Header */ -.l-game-header__amounts-line { - position: relative; - font-size: 1.2rem; -} - -/*#region IP Amount*/ - -.l-game-header__infinity-points { - position: absolute; - left: 75%; -} - -.c-game-header__infinity-points { - font-size: 1.2rem; - width: 22rem; -} +/* #region IP Amount */ .c-game-header__tesseract-available { - border-width: 0.2rem solid; - border-radius: 0; - padding: 1rem; - margin-bottom: 1.2rem; - font-family: TypeWriter, serif; - background: #111117; + font-family: Typewriter, serif; color: #2ebce6; - cursor: pointer; + background: #111117; + border-width: var(--var-border-width, 0.2rem) solid; + margin-bottom: 1.2rem; + padding: 1rem; transition-duration: 0.15s; animation: a-tesseract-shift 5s infinite; + cursor: pointer; } .s-base--dark .c-game-header__tesseract-available { - background: #EEEEEE; + background: #eeeeee; animation: a-tesseract-shift-dark 5s infinite; } .c-infinity-dim-tab__tesseract-available:hover { + color: #306d80; background: black; - color: #00c5ff; } .s-base--dark .c-infinity-dim-tab__tesseract-available:hover { background: white; } - .c-game-header__ip-amount { font-weight: bold; } @@ -1955,20 +2217,15 @@ screen and (max-width: 480px) { text-shadow: 0.1rem 0.1rem 0 black; } -/*#endregion IP Amount*/ +/* #endregion IP Amount */ -/*#region EP Amount*/ +/* #region EP Amount */ .l-game-header__eternity-points { position: absolute; right: 75%; } -.c-game-header__eternity-points { - font-size: 1.2rem; - width: 22rem; -} - .c-game-header__ep-amount { font-weight: bold; } @@ -1989,9 +2246,9 @@ screen and (max-width: 480px) { text-shadow: 0.1rem 0.1rem 0 black; } -/*#endregion EP Amount*/ +/* #endregion EP Amount */ -/*#region c-game-header__antimatter*/ +/* #region c-game-header__antimatter */ .c-game-header__antimatter { font-size: 2.5rem; @@ -2005,10 +2262,10 @@ screen and (max-width: 480px) { } @keyframes a-game-header__antimatter--glow { - 0% {color: #2196f3;} - 33% {color: #673ab7;} - 66% {color: #00bcd4;} - 100% {color: #2196f3;} + 0% { color: #2196f3; } + 33% { color: #673ab7; } + 66% { color: #00bcd4; } + 100% { color: #2196f3; } } .t-s11 .c-game-header__antimatter { @@ -2016,24 +2273,24 @@ screen and (max-width: 480px) { } @keyframes a-game-header__antimatter--glow-blob { - 0% { color: #fbc21b } - 33% { color: #caa32c } - 66% { color: #fba11b } - 100% { color: #fbc21b } + 0% { color: #fbc21b; } + 33% { color: #caa32c; } + 66% { color: #fba11b; } + 100% { color: #fbc21b; } } .t-dark-metro .c-game-header__antimatter { color: #e0e0e0; } -/*#endregion c-game-header__antimatter*/ +/* #endregion c-game-header__antimatter */ .l-game-header__challenge-text { display: flex; - justify-content: center; - align-items: center; height: 2rem; top: 50%; + justify-content: center; + align-items: center; font-size: 1.2rem; } @@ -2041,21 +2298,6 @@ screen and (max-width: 480px) { position: relative; } -.l-game-header__big-crunch-btn { - position: absolute; - left: 75%; -} - -.l-game-header__eternity-btn { - position: absolute; - right: 75%; -} - -.l-game-header__new-dim-btn { - position: absolute; - right: 75%; -} - .c-game-header__tickspeed-row { display: flex; flex-direction: column; @@ -2067,57 +2309,53 @@ screen and (max-width: 480px) { } .u-fa-padding { - width: 1rem; + width: 1.2rem; + align-content: center; } -/*#endregion Header*/ +/* #endregion Header */ .t-s2 .c-dim-tab { color: black; - text-shadow: .05rem .05rem 0 white, -.05rem -.05rem 0 white; + text-shadow: 0.05rem 0.05rem 0 white, -0.05rem -0.05rem 0 white; } -/*#region Dimensions*/ +/* #region Dimensions */ .l-dimensions-container { width: 100%; } +.l-dimension-single-row { + display: grid; + grid-template-columns: repeat(7, 1fr); + width: 100%; + height: 3.5rem; + align-content: center; +} + .c-dim-row--not-reached { opacity: 0.5; } -.c-dim-row__name { - width: 40%; +.l-dim-row-multi-button-container { + display: flex; + grid-column: 5 / 8; + min-width: 25rem; + justify-content: flex-end; } -.c-dim-row__label { - text-align: left; +.l-dim-row-small-text { + vertical-align: middle; + font-size: 1rem; + line-height: 1rem; } -.c-dim-row__label--growable { - flex-grow: 1; - width: 15rem; -} +/* #endregion Dimensions */ -.c-dim-row__label--small { - font-size: 1.2rem; -} +/* #region Antimatter Dimensions */ -.l-dim-row__button { - flex-shrink: 0; - margin-left: 1.6rem; -} - -.l-dim-row__button--right-offset { - margin-right: 10rem; -} - -/*#endregion Dimensions*/ - -/*#region Antimatter Dimensions*/ - -/*#region l-antimatter-dim-tab*/ +/* #region l-antimatter-dim-tab */ .l-antimatter-dim-tab > * { flex-shrink: 0; @@ -2127,9 +2365,9 @@ screen and (max-width: 480px) { margin: 1rem 0; } -/*#endregion l-antimatter-dim-tab*/ +/* #endregion l-antimatter-dim-tab */ -/*#region l-antimatter-dim-tab-header*/ +/* #region l-antimatter-dim-tab-header */ .l-antimatter-dim-tab__header { display: flex; @@ -2139,56 +2377,77 @@ screen and (max-width: 480px) { padding-top: 0.5rem; } -/*#endregion l-antimatter-dim-tab-header*/ +/* #endregion l-antimatter-dim-tab-header */ -/*#region c-antimatter-dim-row*/ +/* #region c-antimatter-dim-row */ + +.c-dimension-row { + align-items: center; + font-size: 1.6rem; + margin-top: 1rem; +} .c-antimatter-dim-row { /* relative because floating text is 'position: absolute' */ position: relative; - display: flex; - flex-direction: row; - align-items: center; - font-size: 1.5rem; - margin-top: 1rem; } -/*#endregion c-dimension-row*/ +.c-antimatter-prestige-row { + display: grid; + flex-direction: row; + grid-template-columns: repeat(7, 1fr); + height: 5rem; + align-content: center; + text-align: left; +} -/*#region c-progress-bar*/ +.l-dim-row__prestige-text { + grid-column: 1 / 6; +} + +.l-dim-row__prestige-button { + grid-column: 6 / 8; +} + +/* #endregion c-dimension-row */ + +/* #region c-progress-bar */ .c-progress-bar { width: 100%; - background-color: #A3A3A3; - border-radius: 0.5rem; - pointer-events: none; + + /* Start fill from left corner */ + text-align: left; + background-color: #a3a3a3; + border-radius: var(--var-border-radius, 0.5rem); margin-right: auto; margin-left: auto; - /* Start fill from left corner*/ - text-align: left; + pointer-events: none; } .c-progress-bar__fill { width: 0; - background-color: #127A20; + + /* Align percents on center */ + text-align: center; + background-color: #127a20; border-radius: inherit; transition-duration: 0.1s; pointer-events: none; - /* Align percents on center*/ - text-align: center; } .c-progress-bar__percents { + overflow-wrap: normal; font-size: 1.6rem; color: black; pointer-events: all; + -webkit-user-select: none; user-select: none; - overflow-wrap: normal; } -/*#region themes*/ +/* #region themes */ -/*#region t-dark t-s6 t-s10*/ +/* #region t-dark t-s6 t-s10 */ .t-dark .c-progress-bar__fill, .t-s6 .c-progress-bar__fill, @@ -2196,9 +2455,9 @@ screen and (max-width: 480px) { background-color: #1b5e20; } -/*#endregion t-dark t-s6 t-s10*/ +/* #endregion t-dark t-s6 t-s10 */ -/*#region t-inverted t-inverted-metro*/ +/* #region t-inverted t-inverted-metro */ .t-inverted .c-progress-bar__fill, .t-inverted-metro .c-progress-bar__fill { @@ -2206,25 +2465,25 @@ screen and (max-width: 480px) { background-color: #ed85df; } -/*#endregion t-inverted t-inverted-metro*/ +/* #endregion t-inverted t-inverted-metro */ -/*#region t-s1*/ +/* #region t-s1 */ .t-s1 .c-progress-bar__fill { background-color: #4baf4e; } -/*#endregion t-s1*/ +/* #endregion t-s1 */ -/*#endregion themes*/ +/* #endregion themes */ -/*#endregion c-progress-bar*/ +/* #endregion c-progress-bar */ -/*#endregion Antimatter Dimensions*/ +/* #endregion Antimatter Dimensions */ -/*#region Infinity Dimensions*/ +/* #region Infinity Dimensions */ -/*#region l-infinity-dim-tab*/ +/* #region l-infinity-dim-tab */ .l-infinity-dim-tab { display: flex; @@ -2242,26 +2501,26 @@ screen and (max-width: 480px) { } .c-infinity-dim-tab__tesseract-button { - padding: 1rem; - margin-bottom: 1.2rem; - font-family: TypeWriter, serif; - background: #111117; + font-family: Typewriter, serif; color: #2ebce6; - border: 0.2rem solid; - border-radius: 0; - cursor: pointer; + background: #111117; + border: var(--var-border-width, 0.2rem) solid; + border-radius: var(--var-border-radius, 0.4rem); + margin-bottom: 1.2rem; + padding: 1rem; transition-duration: 0.15s; animation: a-tesseract-shift 5s infinite; + cursor: pointer; } .s-base--dark .c-infinity-dim-tab__tesseract-button { - background: #EEEEEE; + background: #eeeeee; animation: a-tesseract-shift-dark 5s infinite; } .c-infinity-dim-tab__tesseract-button:hover { - background: black; color: #00c5ff; + background: black; } .s-base--dark .c-infinity-dim-tab__tesseract-button:hover { @@ -2269,44 +2528,44 @@ screen and (max-width: 480px) { } .c-infinity-dim-tab__tesseract-button--disabled { - background: #444040; color: #bfdde8; - cursor: default; + background: #444040; animation: none; + cursor: default; } .s-base--dark .c-infinity-dim-tab__tesseract-button--disabled { - background: #444040; color: #bfdde8; - cursor: default; + background: #444040; animation: none; + cursor: default; } .c-infinity-dim-tab__tesseract-button--disabled:hover { - background: #444040; color: #bfdde8; + background: #444040; } .s-base--dark .c-infinity-dim-tab__tesseract-button--disabled:hover { - background: #444040; color: #bfdde8; + background: #444040; } @keyframes a-tesseract-shift { - 0% {box-shadow: 1.5rem 1.5rem 0.5rem black, -1.5rem -1.5rem 0.5rem black;} - 50% {box-shadow: 1.5rem -1.5rem 0.5rem black, -1.5rem 1.5rem 0.5rem black;} - 100% {box-shadow: -1.5rem -1.5rem 0.5rem black, 1.5rem 1.5rem 0.5rem black;} + 0% { box-shadow: 1.5rem 1.5rem 0.5rem black, -1.5rem -1.5rem 0.5rem black; } + 50% { box-shadow: 1.5rem -1.5rem 0.5rem black, -1.5rem 1.5rem 0.5rem black; } + 100% { box-shadow: -1.5rem -1.5rem 0.5rem black, 1.5rem 1.5rem 0.5rem black; } } @keyframes a-tesseract-shift-dark { - 0% {box-shadow: 1.5rem 1.5rem 0.5rem white, -1.5rem -1.5rem 0.5rem white;} - 50% {box-shadow: 1.5rem -1.5rem 0.5rem white, -1.5rem 1.5rem 0.5rem white;} - 100% {box-shadow: -1.5rem -1.5rem 0.5rem white, 1.5rem 1.5rem 0.5rem white;} + 0% { box-shadow: 1.5rem 1.5rem 0.5rem white, -1.5rem -1.5rem 0.5rem white; } + 50% { box-shadow: 1.5rem -1.5rem 0.5rem white, -1.5rem 1.5rem 0.5rem white; } + 100% { box-shadow: -1.5rem -1.5rem 0.5rem white, 1.5rem 1.5rem 0.5rem white; } } -/*#endregion l-infinity-dim-tab*/ +/* #endregion l-infinity-dim-tab */ -/*#region c-infinity-dim-description__accent*/ +/* #region c-infinity-dim-description__accent */ .c-infinity-dim-description__accent { font-size: 3.5rem; @@ -2315,7 +2574,7 @@ screen and (max-width: 480px) { .t-metro .c-infinity-dim-description__accent, .t-s8 .c-infinity-dim-description__accent { - text-shadow: 0 0 0.1rem rgba(0, 0, 0, 0.5), -0.1rem 0.1rem 0.1rem black; + text-shadow: 0 0 0.1rem rgba(0, 0, 0, 50%), -0.1rem 0.1rem 0.1rem black; } .t-dark .c-infinity-dim-description__accent, @@ -2323,7 +2582,7 @@ screen and (max-width: 480px) { .t-s10 .c-infinity-dim-description__accent, .t-s11 .c-infinity-dim-description__accent { color: white; - text-shadow: 0 0 0.7rem #fff; + text-shadow: 0 0 0.7rem #ffffff; } .t-metro .c-infinity-dim-description__accent, @@ -2332,25 +2591,13 @@ screen and (max-width: 480px) { color: #ff9800; } -/*#endregion c-infinity-dim-description__accent*/ +/* #endregion c-infinity-dim-description__accent */ -/*#region c-infinity-dim-row*/ +/* #endregion Infinity Dimensions */ -.c-infinity-dim-row { - margin-top: 1rem; - display: flex; - flex-direction: row; - align-items: center; - font-size: 1.6rem; -} +/* #region Time Dimensions */ -/*#endregion c-infinity-dim-row*/ - -/*#endregion Infinity Dimensions*/ - -/*#region Time Dimensions*/ - -/*#region l-time-dim-tab*/ +/* #region l-time-dim-tab */ .l-time-dim-tab { display: flex; @@ -2358,9 +2605,9 @@ screen and (max-width: 480px) { align-items: center; } -/*#endregion l-time-dim-tab*/ +/* #endregion l-time-dim-tab */ -/*#region c-time-dim-description__accent*/ +/* #region c-time-dim-description__accent */ .c-time-dim-description__accent { font-size: 3.5rem; @@ -2369,7 +2616,7 @@ screen and (max-width: 480px) { .t-metro .c-time-dim-description__accent, .t-s8 .c-time-dim-description__accent { - text-shadow: 0 0 0.1rem rgba(0, 0, 0, 0.5), -0.1rem 0.1rem 0.1rem black; + text-shadow: 0 0 0.1rem rgba(0, 0, 0, 50%), -0.1rem 0.1rem 0.1rem black; } .t-dark .c-time-dim-description__accent, @@ -2386,23 +2633,11 @@ screen and (max-width: 480px) { color: #673ab7; } -/*#endregion c-time-dim-description__accent*/ +/* #endregion c-time-dim-description__accent */ -/*#region c-time-dim-row*/ +/* #endregion Time Dimensions */ -.c-time-dim-row { - margin-top: 1rem; - display: flex; - flex-direction: row; - align-items: center; - font-size: 1.6rem; -} - -/*#endregion c-time-dim-row*/ - -/*#endregion Time Dimensions*/ - -/*#region Options*/ +/* #region Options */ .l-options-tab { display: flex; @@ -2412,9 +2647,9 @@ screen and (max-width: 480px) { } .c-options-tab__hotkeys-link { - cursor: pointer; width: 22rem; align-self: center; + cursor: pointer; } .c-file-import-button { @@ -2422,21 +2657,21 @@ screen and (max-width: 480px) { } .c-file-import { - position: absolute; - width: 20rem; + width: 100%; height: 5.5rem; - margin: -1.9rem -2rem; + position: absolute; opacity: 0; + margin: -1.9rem -2rem; } .c-file-import::before { - cursor: pointer; + content: " "; font-size: 100rem; padding: 10rem 20rem; - content: ' '; + cursor: pointer; } -/*#region l-options-grid*/ +/* #region l-options-grid */ .l-options-grid { display: flex; @@ -2460,11 +2695,13 @@ screen and (max-width: 480px) { .l-options-grid__notations-header { display: flex; - align-items: center; + flex-direction: row; justify-content: center; + align-items: center; } .c-options-grid__notations { + -webkit-user-select: none; user-select: none; } @@ -2491,40 +2728,70 @@ screen and (max-width: 480px) { box-shadow: 0 0 0.7rem 0.2rem #111111; } -/*#endregion l-options-grid*/ +/* #endregion l-options-grid */ .l-select-notation, .l-select-theme { - display: flex; + overflow: hidden; + background-color: var(--color-base); + border: 0.1rem var(--color-good); + border-style: none solid solid; + border-radius: var(--var-border-radius, 0.4rem); +} + +/* We need scrollbar-color in this rule as well as identical color specifications in the following two; some + modern browsers only support one or the other for scrollbar styling */ +.l-select-notation__inner, +.l-select-theme__inner { + overflow-y: auto; flex-direction: column; - align-items: stretch; + max-height: 24.9rem; + scrollbar-color: var(--color-good-dark) var(--color-disabled); + scrollbar-width: thin; +} + +.l-select-notation__inner::-webkit-scrollbar, +.l-select-theme__inner::-webkit-scrollbar { + width: 0.8rem; + background-color: var(--color-disabled); +} + +.l-select-notation__inner::-webkit-scrollbar-thumb, +.l-select-theme__inner::-webkit-scrollbar-thumb { + background-color: var(--color-good-dark); +} + +.l-select-notation__item:last-child, +.l-select-theme__item:last-child { + border: 0; } .l-select-notation__item, .l-select-theme__item { display: flex; - align-items: center; justify-content: center; + align-items: center; } .o-primary-btn.c-select-notation__item, .o-primary-btn.c-select-theme__item { + border-style: none none solid; + border-radius: 0; box-shadow: none; - border-top: none; } +/* #endregion Options */ -/*#endregion Options*/ +/* #region c-stats-tab */ -/*#region c-stats-tab*/ -/* This the top-level Statistics tab (with all subtabs)*/ +/* This the top-level Statistics tab (with all subtabs) */ .c-stats-tab { display: flex; flex-direction: column; align-items: center; - color: var(--color-text); font-size: 1.2rem; + color: var(--color-text); } .t-dark .c-stats-tab { @@ -2545,9 +2812,9 @@ screen and (max-width: 480px) { } .c-stats-tab-general { - color: var(--color-text); font-size: 2rem; font-weight: bold; + color: var(--color-text); } .c-stats-tab-subheader { @@ -2566,31 +2833,31 @@ screen and (max-width: 480px) { color: var(--color-reality); } -/*#endregion c-stats-tab*/ +/* #endregion c-stats-tab */ -/*#region Past Prestige Runs*/ +/* #region Past Prestige Runs */ .c-past-runs-header { - cursor: pointer; - user-select: none; - display: flex; + resize: none; + flex: auto; flex-direction: row; justify-content: center; - flex: auto; - resize: none; align-items: baseline; + -webkit-user-select: none; + user-select: none; + cursor: pointer; } .o-run-drop-down-icon { - font-size: 1.5em; - margin-left: 2rem; + font-size: 1.5rem; margin-right: 2rem; + margin-left: 2rem; } -/*#endregion Past Prestige Runs*/ +/* #endregion Past Prestige Runs */ -/*#region Challenge Records*/ +/* #region Challenge Records */ .l-challenge-records-tab { display: flex; @@ -2604,54 +2871,83 @@ screen and (max-width: 480px) { margin-left: 2rem; } -/*#endregion Challenge Records*/ +/* #endregion Challenge Records */ -/*#region Speedrun Stats*/ +/* #region Glyph Set Records */ + +.l-glyph-set-tab { + display: flex; + flex-flow: row wrap; + width: 75%; + justify-content: center; + align-items: center; + margin: auto; + margin-top: 0; +} + +.l-glyph-set-entry { + display: flex; + flex-direction: column; + width: 35rem; + height: 12rem; + justify-content: space-between; + font-size: 1.4rem; + color: var(--color-text); + border: var(--var-border-width, 0.2rem) solid var(--color-text); + border-radius: var(--var-border-radius, 0.5rem); + margin: 0.2rem; + padding: 0.2rem; +} + +/* #endregion Glyph Set Records */ + +/* #region Speedrun Stats */ .l-speedrun-milestone-tab { display: flex; - margin: auto; - width: 100%; - flex-direction: row; - flex-wrap: wrap; + flex-flow: row wrap; + width: 95%; justify-content: center; align-items: center; + margin: auto; } .l-speedrun-milestone-entry { display: flex; flex-direction: column; - color: var(--color-text); width: 25rem; height: 8rem; justify-content: space-between; + color: var(--color-text); + border: var(--var-border-width, 0.2rem) solid var(--color-text); + border-radius: var(--var-border-radius, 0.5rem); margin: 0.2rem; padding: 0.2rem; - border: 0.2rem solid var(--color-text); } .l-speedrun-milestone-entry--completed { background-color: var(--color-good); } -/*#endregion Speedrun Stats*/ +/* #endregion Speedrun Stats */ -/*#region Achievements*/ +/* #region Achievements */ -/*#region o-achievement*/ +/* #region o-achievement */ .o-achievement { - position: relative; width: 10.6rem; height: 10.6rem; - border-radius: 0.8rem; - margin: 0 auto; + position: relative; text-align: center; - font-family: TypeWriter, serif; - color: black; + font-family: Typewriter, serif; font-size: 0.8rem; + color: black; + border: var(--var-border-width, 0.2rem) solid; + border-radius: var(--var-border-radius, 0.8rem); + margin: 0 auto; + -webkit-user-select: none; user-select: none; - border: 0.2rem solid; } .o-achievement--normal { @@ -2687,8 +2983,8 @@ screen and (max-width: 480px) { } .o-achievement--hidden { + background-color: #555555; background-image: url("../images/achhidden.png"); - background-color: #555; border-color: black; } @@ -2703,58 +2999,57 @@ screen and (max-width: 480px) { } .o-achievement:hover .o-achievement__tooltip { - opacity: 1; bottom: 11rem; + opacity: 1; } -.o-achievement:hover .o-achievement__tooltip:after { +.o-achievement:hover .o-achievement__tooltip::after { border-top-width: 0.7rem; margin-bottom: -0.7rem; } .o-achievement__tooltip { - opacity: 0; - transition-duration: 0.3s; + width: 20rem; + position: absolute; + bottom: 10.2rem; + z-index: 2; font-size: 1.4rem; - border: 0.1rem solid black; - border-radius: 0.8rem; + opacity: 0; color: var(--color-text); background: var(--color-base); - width: 20rem; + border: 0.1rem solid black; + border-radius: var(--var-border-radius, 0.8rem); margin-left: -5rem; padding: 0.4rem; - z-index: 2; + transition-duration: 0.3s; pointer-events: none; - position: absolute; - bottom: 10.2rem } -.o-achievement__tooltip:after { +.o-achievement__tooltip::after { + content: " "; + width: 0; position: absolute; bottom: 0; left: 50%; - margin-left: -0.7rem; - margin-bottom: 0; - width: 0; + z-index: 0; border-top: 0 solid black; border-right: 0.7rem solid transparent; border-left: 0.7rem solid transparent; - content: " "; + margin-bottom: 0; + margin-left: -0.7rem; transition-duration: 0.3s; - z-index: 0; } .s-base--dark .o-achievement__tooltip { - border: 0.1rem solid var(--color-text); - background-color: #111111; + background: #111111; + border-color: var(--color-text); } -.s-base--dark .o-achievement__tooltip:after { +.s-base--dark .o-achievement__tooltip::after { border-top-color: var(--color-text); } .s-base--metro .o-achievement__tooltip { - border-radius: 0; bottom: 10.4rem; } @@ -2764,12 +3059,12 @@ screen and (max-width: 480px) { .t-s6 .o-achievement__tooltip, .t-s10 .o-achievement__tooltip { - border: 0.1rem solid white; background: black; + border-color: white; } -.t-s6 .o-achievement__tooltip:after, -.t-s10 .o-achievement__tooltip:after { +.t-s6 .o-achievement__tooltip::after, +.t-s10 .o-achievement__tooltip::after { border-top-color: white; } @@ -2789,23 +3084,21 @@ screen and (max-width: 480px) { } .o-achievement__indicator { - position: absolute; - bottom: 0; - right: 0; width: 1.5rem; height: 1.5rem; + position: absolute; + right: 0; + bottom: 0; font-size: 1rem; color: black; background: #5ac467; - border-left: 0.2rem solid #127a20; - border-top: 0.2rem solid #127a20; - border-top-left-radius: 0.8rem; - border-bottom-right-radius: 0.6rem; + border-top: var(--var-border-width, 0.2rem) solid #127a20; + border-left: var(--var-border-width, 0.2rem) solid #127a20; + border-top-left-radius: var(--var-border-radius, 0.8rem); + border-bottom-right-radius: var(--var-border-radius, 0.6rem); } .s-base--metro .o-achievement__indicator { - border-width: 0.1rem; - border-radius: 0; background: #66bb6a; border-color: #43a047; } @@ -2825,7 +3118,7 @@ screen and (max-width: 480px) { border-color: var(--color-bad); } -.s-base--metro .o-achievement__indicator--locked { +.s-base--metro .o-achievement__indicator--locked { background: #a3a3a3; border-color: var(--color-bad); } @@ -2834,6 +3127,10 @@ screen and (max-width: 480px) { background: #9e9e9e; } +.o-achievement--hidden .o-achievement__indicator--locked { + border-color: black; +} + .o-achievement__indicator--waiting { background: #d1d161; border-color: #acac39; @@ -2849,26 +3146,17 @@ screen and (max-width: 480px) { border-color: #7d7d36; } -.o-achievement__indicator--secret { - background: #555; - border-color: black; -} +/* #region themes */ -/*#region themes*/ - -/*#region t-dark*/ +/* #region t-dark */ .t-dark .o-achievement--unlocked { background-color: #43a047; } -/*#endregion t-dark*/ +/* #endregion t-dark */ -/*#region t-dark-metro*/ - -.t-dark-metro .o-achievement { - border-width: 0.1rem; -} +/* #region t-dark-metro */ .t-dark-metro .o-achievement--unlocked { background-color: #4caf50; @@ -2884,96 +3172,91 @@ screen and (max-width: 480px) { border-color: #7d7d36; } -/*#endregion t-dark-metro*/ +/* #endregion t-dark-metro */ -/*#region t-metro t-inverted-metro t-s8*/ - -.s-base--metro .o-achievement { - border-width: 0.1rem; - border-radius: 0; -} +/* #region t-metro t-inverted-metro t-s8 */ .t-metro .o-achievement--unlocked, .t-inverted-metro .o-achievement--unlocked, -.t-s8 .o-achievement--unlocked { +.t-s8 .o-achievement--unlocked { background-color: #66bb6a; border-color: #43a047; } .t-metro .o-achievement--locked, .t-inverted-metro .o-achievement--locked, -.t-s8 .o-achievement--locked { +.t-s8 .o-achievement--locked { background-color: #9e9e9e; border-color: var(--color-bad); } .t-metro .o-achievement--waiting, .t-inverted-metro .o-achievement--waiting, -.t-s8 .o-achievement--waiting { +.t-s8 .o-achievement--waiting { background-color: #ffee58; border-color: #757575; } -/*#endregion t-metro t-dark-metro t-s8*/ +/* #endregion t-metro t-dark-metro t-s8 */ -/*#region t-s1*/ +/* #region t-s1 */ .t-s1 .o-achievement--unlocked { background-color: #dbd242; border-color: #c5ba26; } -/*#endregion t-s1*/ +/* #endregion t-s1 */ -/*#region t-s2*/ +/* #region t-s2 */ .t-s2 .o-achievement--locked { - background-color: rgba(0, 0, 0, 0); + background-color: rgba(0, 0, 0, 0%); } -/*#endregion t-s2*/ +/* #endregion t-s2 */ -/*#region t-s5*/ +/* #region t-s5 */ .t-s5 .o-achievement--unlocked { - border-color: #bbb; + border-color: #bbbbbb; } .t-s5 .o-achievement--locked { - border-color: #000; - background-color: #222; + background-color: #222222; + border-color: #000000; } -/*#endregion t-s5*/ +/* #endregion t-s5 */ -/*#region t-s6 t-s10*/ +/* #region t-s6 t-s10 */ .t-s6 .o-achievement--unlocked, .t-s10 .o-achievement--unlocked { background-color: #43a047; } -/*#endregion t-s6 t-s10*/ +/* #endregion t-s6 t-s10 */ -/*#region t-s7*/ +/* #region t-s7 */ .t-s7 .o-achievement--unlocked { background-color: #bbbbbb; - border-color: #666; + border-color: #666666; } .t-s7 .o-achievement--locked { - background-color: #555; - border-color: #111; + background-color: #555555; + border-color: #111111; } -/*#endregion t-s7*/ +/* #endregion t-s7 */ -/*#endregion themes*/ +/* #endregion themes */ -/*#endregion o-achievement*/ +/* #endregion o-achievement */ -/*#region c-achievements-tab*/ +/* #region c-achievements-tab */ .l-achievements-tab { display: flex; @@ -2982,28 +3265,29 @@ screen and (max-width: 480px) { } .c-achievements-tab__header { - font-size: 1.5rem; - margin-top: 0.6rem; + display: inline; width: 60rem; position: relative; - display: inline; justify-content: center; + font-size: 1.5rem; + margin-top: 0.6rem; + -webkit-user-select: none; user-select: none; } .t-s4 .c-achievements-tab__header--multipliers::after { content: "you can click the colon at the end of this line in any other theme to turn on these achievement images"; - font-size: 0.8rem; - position: absolute; - margin-left: 16rem; - margin-top: -9rem; width: 20rem; + position: absolute; + font-size: 0.8rem; + margin-top: -9rem; + margin-left: 16rem; } .c-achievements-tab__timer { - color: black; - font-size: 1.2rem; width: 40%; + font-size: 1.2rem; + color: black; margin: 0.5rem auto; } @@ -3011,9 +3295,9 @@ screen and (max-width: 480px) { color: #757575; } -/*#endregion c-achievements-tab*/ +/* #endregion c-achievements-tab */ -/*#region achievement-grid*/ +/* #region achievement-grid */ .l-achievement-grid { display: flex; @@ -3033,12 +3317,11 @@ screen and (max-width: 480px) { .c-achievement-grid__row--completed { background-color: #007308; - border-radius: 10px; + border-radius: var(--var-border-radius, 10px); } .s-base--metro .c-achievement-grid__row--completed { background-color: #1b5e20; - border-radius: 0; } .t-s1 .c-achievement-grid__row--completed { @@ -3046,16 +3329,16 @@ screen and (max-width: 480px) { } .t-s7 .c-achievement-grid__row--completed { - background-color: #aaa; + background-color: #aaaaaa; } -/*#endregion achievement-grid*/ +/* #endregion achievement-grid */ -/*#endregion Achievements*/ +/* #endregion Achievements */ -/*#region Challenges*/ +/* #region Challenges */ -/*#region l-challenges-tab*/ +/* #region l-challenges-tab */ .l-challenges-tab { display: flex; @@ -3064,52 +3347,53 @@ screen and (max-width: 480px) { } .l-challenges-tab__header { - margin: 0.5rem 0; display: flex; flex-direction: column; justify-content: center; align-items: center; + margin: 0.5rem 0; } .o-challenges-tab__header-toggle { - cursor: pointer; - user-select: none; display: inline-flex; flex-direction: row; - align-items: center; justify-content: flex-start; + align-items: center; margin: 0 1rem; + -webkit-user-select: none; + user-select: none; + cursor: pointer; } .l-challenges-tab__auto-ec-info { - left: 100%; display: flex; - flex-direction: row; - align-items: center; white-space: nowrap; + flex-direction: row; + left: 100%; + align-items: center; } .c-challenges-tab__auto-ec-info { - font-weight: bold; text-align: left; + font-weight: bold; } .l-challenges-tab__auto-ec-timers { - margin-left: 0.4rem; display: flex; flex-direction: column; + margin-left: 0.4rem; } -/*#endregion l-challenges-tab*/ +/* #endregion l-challenges-tab */ -/*#region l-challenge-grid*/ +/* #region l-challenge-grid */ .l-challenge-grid { - width: 90rem; display: flex; - flex-direction: row; + flex-flow: row wrap; + width: 90rem; justify-content: center; - flex-wrap: wrap; + padding-right: 1rem; } .l-challenge-grid__cell { @@ -3120,147 +3404,74 @@ screen and (max-width: 480px) { display: none; } -/*#endregion l-challenge-grid*/ +/* #endregion l-challenge-grid */ -/*#region c-challenge-box*/ +/* #region c-challenge-box */ .c-challenge-box { width: 40rem; height: 13rem; - padding: 0.5rem 0.4rem; - box-sizing: border-box; - font-weight: bold; - border: 0.2rem solid black; - border-radius: 0.5rem; font-size: 1rem; + font-weight: bold; + border: var(--var-border-width, 0.2rem) solid black; + border-radius: var(--var-border-radius, 0.5rem); + box-sizing: border-box; + padding: 0.5rem 0.4rem; transition: all 0.2s, visibility 0s; } .c-challenge-box--normal { - color: var(--color-text); font-size: 1.05rem; - background-color: #f2f2f2; + color: var(--color-text); + background-color: var(--color-base); } .c-challenge-box--infinity { height: 18rem; - color: white; - background-color: #181818; + color: var(--color-text); + background-color: var(--color-prestige--accent); + border-color: var(--color-infinity); } .c-challenge-box--eternity { height: 18rem; - color: var(--color-eternity); - background-color: black; - border: 0.3rem solid var(--color-eternity); - border-radius: 1rem; + color: var(--color-text); + background-color: var(--color-prestige--accent); + border-color: var(--color-eternity); + border-radius: var(--var-border-radius, 1rem); } .c-challenge-box__reward-description--small-text { - font-size: .95rem; + font-size: 0.95rem; } -/*#region themes*/ +/* #region themes */ -/*#region t-dark*/ - -.t-dark .c-challenge-box--normal { - background-color: #27343b; -} - -/*#endregion t-dark*/ - -/*#region t-metro t-dark-metro t-inverted-metro t-s8*/ - -.s-base--metro .c-challenge-box { - border-radius: 0; - border: 0.1rem solid; -} - -.s-base--metro .c-challenge-box--normal { - border-color: black; -} - -.s-base--metro .c-challenge-box--infinity { - border-color: var(--color-infinity); -} - -.s-base--metro .c-challenge-box--eternity { - border-color: var(--color-eternity); -} - -.t-dark-metro .c-challenge-box--normal { - background-color: #27343b; -} - -/*#endregion t-metro t-dark-metro t-inverted-metro*/ - -/*#region t-s1*/ - -.t-s1 .c-challenge-box--normal, -.t-s1 .c-challenge-box--infinity { - background-color: #dbd242; -} - -/*#endregion t-s1*/ - -/*#region t-s2*/ - -.t-s2 .c-challenge-box--normal { - color: white; -} - -/*#endregion t-s2*/ - -/*#region t-s6 t-s10*/ - -.t-s6 .c-challenge-box--normal, -.t-s6 .c-challenge-box--infinity, -.t-s10 .c-challenge-box--normal, -.t-s10 .c-challenge-box--infinity, -.t-s11 .c-challenge-box--normal, -.t-s11 .c-challenge-box--infinity { - background-color: black; - border: 0.1rem solid; -} - -.t-s6 .c-challenge-box--normal, -.t-s10 .c-challenge-box--normal { - color: #e0e0e0; - border-color: grey; -} - -.t-s6 .c-challenge-box--infinity, -.t-s10 .c-challenge-box--infinity { - border-color: white; -} - -/*#endregion t-s6 t-s10*/ - -/*#region t-s7*/ +/* #region t-s7 */ .t-s7 .c-challenge-box { color: black; - border: 0.1rem solid #9b9b9b; background: white; + border-color: #9b9b9b; } -/*#endregion t-s7*/ +/* #endregion t-s7 */ -/*#endregion themes*/ +/* #endregion themes */ -/*#endregion c-challenge-box*/ +/* #endregion c-challenge-box */ -/*#region l-challenge-box*/ +/* #region l-challenge-box */ .l-challenge-box { - margin-top: 0.5rem; display: flex; flex-direction: column; - justify-content: space-between; - align-items: center; + /* because of hint-text */ position: relative; + justify-content: space-between; + align-items: center; + margin-top: 0.5rem; } .l-challenge-box > * { @@ -3274,23 +3485,23 @@ screen and (max-width: 480px) { .l-challenge-box__bottom--infinity { display: flex; flex-direction: column; + height: 5.5rem; justify-content: flex-end; align-items: center; - height: 5.5rem; } -/*#endregion l-challenge-box*/ +/* #endregion l-challenge-box */ -/*#region o-challenge-btn*/ +/* #region o-challenge-btn */ .o-challenge-btn { width: 15rem; height: 3rem; font-family: Typewriter, serif; - font-weight: bold; - border: 0.3rem solid #127a20; - border-radius: 1rem; font-size: 1.2rem; + font-weight: bold; + border: var(--var-border-width, 0.3rem) solid #127a20; + border-radius: var(--var-border-radius, 1rem); margin-bottom: 0.5rem; transition: all 0.2s, visibility 0s; cursor: pointer; @@ -3327,9 +3538,9 @@ screen and (max-width: 480px) { background-color: #17a32e; } -/*#region themes*/ +/* #region themes */ -/*#region t-dark*/ +/* #region t-dark */ .t-dark .o-challenge-btn--unlocked { background-color: #546e7a; @@ -3341,8 +3552,8 @@ screen and (max-width: 480px) { } .t-dark .o-challenge-btn--completed { - box-shadow: none; border-color: #388e3c; + box-shadow: none; } .t-dark .o-challenge-btn--running { @@ -3359,19 +3570,15 @@ screen and (max-width: 480px) { border-color: #b84b5f; } -/*#endregion t-dark*/ +/* #endregion t-dark */ -/*#region t-metro t-dark-metro t-inverted-metro t-s8*/ - -.s-base--metro .o-challenge-btn { - border-radius: 0; -} +/* #region t-metro t-dark-metro t-inverted-metro t-s8 */ .s-base--metro .o-challenge-btn--unlocked, .s-base--metro .o-challenge-btn--completed { - border: 0.3rem solid #43a047; - border-bottom-color: #388e3c; + border: 0.1rem solid #43a047; border-right-color: #388e3c; + border-bottom-color: #388e3c; } .s-base--metro .o-challenge-btn--unlocked:hover, @@ -3386,28 +3593,28 @@ screen and (max-width: 480px) { .s-base--metro .o-challenge-btn--locked { background-color: #9e9e9e; - border: 0.3rem solid #757575; - border-bottom-color: #616161; + border: 0.1rem solid #757575; border-right-color: #616161; + border-bottom-color: #616161; } .t-metro .o-challenge-btn--redo, .t-inverted-metro .o-challenge-btn--redo, .t-s8 .o-challenge-btn--redo { background-color: #4b8a4e; - border: 0.3rem solid #757575; - border-bottom-color: #616161; + border: 0.1rem solid #757575; border-right-color: #616161; + border-bottom-color: #616161; } -/*#endregion t-metro t-dark-metro t-inverted-metro t-s8*/ +/* #endregion t-metro t-dark-metro t-inverted-metro t-s8 */ -/*#region t-s1*/ +/* #region t-s1 */ .t-s1 .o-challenge-btn--unlocked { color: white; background: none; - border: 0.3rem solid #507751; + border: var(--var-border-width, 0.3rem) solid #507751; } .t-s1 .o-challenge-btn--unlocked:hover, @@ -3415,9 +3622,9 @@ screen and (max-width: 480px) { background-color: #639565; } -/*#endregion t-s1*/ +/* #endregion t-s1 */ -/*#region t-s6 t-s10*/ +/* #region t-s6 t-s10 */ .t-s6 .o-challenge-btn, .t-s10 .o-challenge-btn { @@ -3460,15 +3667,15 @@ screen and (max-width: 480px) { border-color: #b84b5f; } -/*#endregion t-s6 t-s10*/ +/* #endregion t-s6 t-s10 */ -/*#endregion themes*/ +/* #endregion themes */ -/*#endregion o-challenge-btn*/ +/* #endregion o-challenge-btn */ -/*#endregion Challenges*/ +/* #endregion Challenges */ -/*#region infinity-tab*/ +/* #region infinity-tab */ .c-infinity-tab__header { font-size: 1.5rem; @@ -3479,9 +3686,9 @@ screen and (max-width: 480px) { color: var(--color-infinity); } -/*#region themes*/ +/* #region themes */ -/*#region t-dark t-s6 t-s10*/ +/* #region t-dark t-s6 t-s10 */ .t-dark .c-infinity-tab__infinity-points, .t-s6 .c-infinity-tab__infinity-points, @@ -3490,23 +3697,23 @@ screen and (max-width: 480px) { text-shadow: 0 0 0.7rem; } -/*#endregion t-dark t-s6 t-s10*/ +/* #endregion t-dark t-s6 t-s10 */ -/*#region t-s1*/ +/* #region t-s1 */ .t-s1 .c-infinity-tab__infinity-points { text-shadow: 0.1rem 0.1rem 0 black; } -/*#endregion t-s1*/ +/* #endregion t-s1 */ -/*#endregion themes*/ +/* #endregion themes */ -/*#endregion infinity-tab*/ +/* #endregion infinity-tab */ -/*#region Infinity Upgrades*/ +/* #region Infinity Upgrades */ -/*#region l-infinity-upgrades-tab*/ +/* #region l-infinity-upgrades-tab */ .l-infinity-upgrades-tab { display: flex; @@ -3522,51 +3729,42 @@ screen and (max-width: 480px) { margin: 0.5rem 0.8rem; } -/*#endregion l-infinity-upgrades-tab*/ +/* #endregion l-infinity-upgrades-tab */ -/*#region l-infinity-upgrade-grid*/ +/* #region l-infinity-upgrade-grid */ .l-infinity-upgrade-grid { display: flex; flex-direction: row; } -.l-infinity-upgrade-grid__column { - display: flex; - flex-direction: column; -} - .l-infinity-upgrade-grid__cell { margin: 0.5rem 0.8rem; } -/*#endregion l-infinity-upgrade-grid*/ +/* #endregion l-infinity-upgrade-grid */ .l-infinity-upgrades-bottom-row { display: flex; margin-top: 1rem; } -/*#region o-infinity-upgrade-btn*/ +/* #region o-infinity-upgrade-btn */ .o-infinity-upgrade-btn { - color: white; - background-color: #1f1f1f; - font-weight: bold; - font-size: 1rem; - border: 0.1rem solid black; width: 19rem; height: 9rem; - transition-duration: 0.2s; - border-radius: 0.4rem; font-family: Typewriter, serif; + font-size: 1rem; + font-weight: bold; + color: white; + background-color: #1f1f1f; + border: 0.1rem solid black; + border-radius: var(--var-border-radius, 0.4rem); + transition-duration: 0.2s; cursor: pointer; } -.s-base--metro .o-infinity-upgrade-btn { - border-radius: 0; -} - .t-s6 .o-infinity-upgrade-btn, .t-s10 .o-infinity-upgrade-btn { box-shadow: 0 0 0.7rem 0.2rem #111111; @@ -3576,22 +3774,12 @@ screen and (max-width: 480px) { color: var(--color-text-inverted); } -/*#region o-infinity-upgrade-btn--available*/ +/* #region o-infinity-upgrade-btn--available */ .o-infinity-upgrade-btn--available:hover { background-color: #a53b98; } -.o-infinity-upgrade-btn--useless-available { - background-color: var(--color-pelle--base); - color: black; -} - -.o-infinity-upgrade-btn--useless-available:hover { - background-color: var(--color-pelle-secondary); - color: black; -} - .t-dark .o-infinity-upgrade-btn--available:hover, .t-s6 .o-infinity-upgrade-btn--available:hover, .t-s10 .o-infinity-upgrade-btn--available:hover { @@ -3602,9 +3790,9 @@ screen and (max-width: 480px) { background-color: #9c27b0; } -/*#endregion o-infinity-upgrade-btn--available*/ +/* #endregion o-infinity-upgrade-btn--available */ -/*#region o-infinity-upgrade-btn--color-2*/ +/* #region o-infinity-upgrade-btn--color-2 */ .o-infinity-upgrade-btn--color-2.o-infinity-upgrade-btn--available:hover { background-color: #ff3300; @@ -3620,9 +3808,9 @@ screen and (max-width: 480px) { background-color: #f44336; } -/*#endregion o-infinity-upgrade-btn--color-2*/ +/* #endregion o-infinity-upgrade-btn--color-2 */ -/*#region o-infinity-upgrade-btn--color-3*/ +/* #region o-infinity-upgrade-btn--color-3 */ .o-infinity-upgrade-btn--color-3.o-infinity-upgrade-btn--available:hover { background-color: #c6d50d; @@ -3638,9 +3826,9 @@ screen and (max-width: 480px) { background-color: #ffeb3b; } -/*#endregion o-infinity-upgrade-btn--color-3*/ +/* #endregion o-infinity-upgrade-btn--color-3 */ -/*#region o-infinity-upgrade-btn--color-4*/ +/* #region o-infinity-upgrade-btn--color-4 */ .o-infinity-upgrade-btn--color-4.o-infinity-upgrade-btn--available:hover { background-color: #0cfec5; @@ -3656,23 +3844,12 @@ screen and (max-width: 480px) { background-color: #00bcd4; } -/*#endregion o-infinity-upgrade-btn--color-4*/ - -/*#region o-infinity-upgrade-btn--multiplier*/ +/* #endregion o-infinity-upgrade-btn--color-4 */ +/* #region o-infinity-upgrade-btn--multiplier */ .o-infinity-upgrade-btn--multiplier.c-pelle-useless--available { - filter: grayscale(25%) -} - -.o-infinity-upgrade-btn--multiplier.o-infinity-upgrade-btn--useless-available { - background-color: var(--color-pelle--base); - color: black; -} - -.o-infinity-upgrade-btn--multiplier.o-infinity-upgrade-btn--useless-available:hover { - color: black; - background-color: var(--color-pelle-secondary); + filter: grayscale(25%); } .o-infinity-upgrade-btn--multiplier.o-infinity-upgrade-btn--available { @@ -3709,35 +3886,29 @@ screen and (max-width: 480px) { } .t-s1 .o-infinity-upgrade-btn--multiplier.o-infinity-upgrade-btn--available { - background-color: black; color: #d72621; + background-color: black; border-color: #dbd242; } .t-s1 .o-infinity-upgrade-btn--multiplier.o-infinity-upgrade-btn--available:hover { - background-color: white; color: black; + background-color: white; } -/*#endregion o-infinity-upgrade-btn--multiplier*/ +/* #endregion o-infinity-upgrade-btn--multiplier */ -/*#region o-infinity-upgrade-btn--unavailable*/ - -.o-infinity-upgrade-btn--useless-unavailable { - color: #f7f7f7; - background-color: var(--color-pelle--base); - filter: grayscale(90%); -} - -.o-infinity-upgrade-btn--useless-unavailable:hover { - cursor: default; -} +/* #region o-infinity-upgrade-btn--unavailable */ .o-infinity-upgrade-btn--unavailable { color: black; background-color: #f7f7f7; } +.o-infinity-upgrade-btn--unclickable { + cursor: auto; +} + .o-infinity-upgrade-btn--unavailable:hover { color: var(--color-text); background-color: var(--color-text-inverted); @@ -3750,7 +3921,7 @@ screen and (max-width: 480px) { } .t-dark .o-infinity-upgrade-btn--unavailable:hover { - background-color: #37474F; + background-color: #37474f; } .s-base--metro .o-infinity-upgrade-btn--unavailable { @@ -3781,12 +3952,12 @@ screen and (max-width: 480px) { .t-s6 .o-infinity-upgrade-btn--unavailable:hover, .t-s10 .o-infinity-upgrade-btn--unavailable:hover { - background-color: #222; + background-color: #222222; } -/*#endregion o-infinity-upgrade-btn--unavailable*/ +/* #endregion o-infinity-upgrade-btn--unavailable */ -/*#region o-infinity-upgrade-btn--bought*/ +/* #region o-infinity-upgrade-btn--bought */ .o-infinity-upgrade-btn--bought { color: black; @@ -3798,18 +3969,18 @@ screen and (max-width: 480px) { color: black; } -.o-infinity-upgrade-btn--useless-bought { +.o-infinity-upgrade-btn--useless { color: black; background-color: var(--color-pelle--base); filter: grayscale(50%); cursor: default; } -.o-infinity-upgrade-btn--useless-bought:hover { +.o-infinity-upgrade-btn--useless:hover { color: black; } -/*#endregion o-infinity-upgrade-btn--bought*/ +/* #endregion o-infinity-upgrade-btn--bought */ .o-infinity-upgrade-btn--chargeable { color: var(--color-teresa--base); @@ -3829,15 +4000,15 @@ screen and (max-width: 480px) { } @keyframes a-charged-infinity-upgrade-glow { - 0% { box-shadow: inset 0 0 2rem 0} - 13% { box-shadow: inset 0 0 2rem 0.15rem} - 25% { box-shadow: inset 0 0 2rem 0.4rem} - 38% { box-shadow: inset 0 0 2rem 0.65rem} - 50% { box-shadow: inset 0 0 2rem 0.8rem} - 63% { box-shadow: inset 0 0 2rem 0.65rem} - 75% { box-shadow: inset 0 0 2rem 0.4rem} - 88% { box-shadow: inset 0 0 2rem 0.15rem} - 100% { box-shadow: inset 0 0 2rem 0} + 0% { box-shadow: inset 0 0 2rem 0; } + 13% { box-shadow: inset 0 0 2rem 0.15rem; } + 25% { box-shadow: inset 0 0 2rem 0.4rem; } + 38% { box-shadow: inset 0 0 2rem 0.65rem; } + 50% { box-shadow: inset 0 0 2rem 0.8rem; } + 63% { box-shadow: inset 0 0 2rem 0.65rem; } + 75% { box-shadow: inset 0 0 2rem 0.4rem; } + 88% { box-shadow: inset 0 0 2rem 0.15rem; } + 100% { box-shadow: inset 0 0 2rem 0; } } .o-infinity-upgrade-btn--charged { @@ -3856,13 +4027,13 @@ screen and (max-width: 480px) { color: var(--color-teresa--accent); } -/*#endregion o-infinity-upgrade-btn*/ +/* #endregion o-infinity-upgrade-btn */ -/*#endregion Infinity Upgrades*/ +/* #endregion Infinity Upgrades */ -/*#region Autobuyers*/ +/* #region Autobuyers */ -/*#region l-autobuyers-tab*/ +/* #region l-autobuyers-tab */ .l-autobuyers-tab { display: flex; @@ -3871,13 +4042,13 @@ screen and (max-width: 480px) { } .l-autobuyers-tab td { - padding: 0; text-align: center; + padding: 0; } -/*#endregion l-autobuyers-tab*/ +/* #endregion l-autobuyers-tab */ -/*#region l-autobuyer-grid*/ +/* #region l-autobuyer-grid */ .l-autobuyer-grid { display: flex; @@ -3890,40 +4061,41 @@ screen and (max-width: 480px) { justify-content: center; } -/*#endregion l-autobuyer-grid*/ +/* #endregion l-autobuyer-grid */ -/*#region o-autobuyer-input*/ +/* #region o-autobuyer-input */ .o-autobuyer-input { - font-size: 1.3rem; - font-family: typewriter; - border: 0.1rem solid #c2c2c2; - padding: 0.5rem; height: 3rem; + font-family: typewriter; + font-size: 1.3rem; + border: 0.1rem solid #c2c2c2; + border-radius: var(--var-border-radius, 0.4rem); box-sizing: border-box; + padding: 0.5rem; } .t-metro .o-autobuyer-input { color: black; - border: 0.1rem solid #A9A9A9; + border-color: #a9a9a9; } .t-dark .o-autobuyer-input, .t-dark-metro .o-autobuyer-input { - background-color: #455A64; - border: 0.1rem solid black; + background-color: #455a64; + border-color: black; } .t-s1 .o-autobuyer-input { background-color: var(--color-base); - border: 0.1rem solid black; + border-color: black; } .t-s6 .o-autobuyer-input, .t-s10 .o-autobuyer-input { color: white; background-color: black; - border: 0.1rem solid #ccc; + border-color: #cccccc; } .o-autobuyer-input--invalid { @@ -3935,39 +4107,40 @@ screen and (max-width: 480px) { background-color: var(--color-bad); } -/*#endregion o-autobuyer-input*/ +/* #endregion o-autobuyer-input */ -/*#region o-autobuyer-toggle-checkbox*/ +/* #region o-autobuyer-toggle-checkbox */ .o-autobuyer-toggle-checkbox { display: flex; flex-direction: row; - align-items: center; justify-content: center; + align-items: center; + -webkit-user-select: none; user-select: none; } .o-autobuyer-toggle-checkbox__label { display: flex; + width: 2rem; + height: 2rem; justify-content: center; align-items: center; - background: var(--color-gh-purple); font-size: 1rem; - height: 2rem; - width: 2rem; - border: 0.2rem solid #383232; - border-top-right-radius: 0.3rem; - border-bottom-left-radius: 1rem; - color: black; font-weight: bold; - cursor: pointer; - user-select: none; + color: black; + background: var(--color-gh-purple); + border: var(--var-border-width, 0.2rem) solid #383232; + border-top-right-radius: var(--var-border-radius, 0.3rem); + border-bottom-left-radius: var(--var-border-radius, 1rem); transition-duration: 0.2s; + -webkit-user-select: none; + user-select: none; + cursor: pointer; } .s-base--metro .o-autobuyer-toggle-checkbox__label { - border: 0.1rem solid black; - border-radius: 0; + border-color: black; } .t-dark .o-autobuyer-toggle-checkbox__label { @@ -3975,8 +4148,7 @@ screen and (max-width: 480px) { } .t-dark-metro .o-autobuyer-toggle-checkbox__label { - border: 0.1rem solid var(--color-base); - border-radius: 0; + border-color: var(--color-base); } .t-s1 .o-autobuyer-toggle-checkbox__label { @@ -3985,7 +4157,7 @@ screen and (max-width: 480px) { .t-s6 .o-autobuyer-toggle-checkbox__label, .t-s10 .o-autobuyer-toggle-checkbox__label { - border: 0.1rem solid #ccc; + border-color: #cccccc; } .o-autobuyer-toggle-checkbox__label:hover { @@ -4013,20 +4185,20 @@ screen and (max-width: 480px) { background: var(--color-bad); } -/*#endregion o-autobuyer-toggle-checkbox*/ +/* #endregion o-autobuyer-toggle-checkbox */ -/*#region o-autobuyer-btn*/ +/* #region o-autobuyer-btn */ .o-autobuyer-btn { width: 17.5rem; height: 3.5rem; - color: white; - background: #1f1f1f; - border: 0.1rem solid #ed85df; font-family: Typewriter; - font-weight: bold; font-size: 0.9rem; - border-radius: 0.4rem; + font-weight: bold; + color: var(--color-text); + background: var(--color-base); + border: 0.1rem solid #ed85df; + border-radius: var(--var-border-radius, 0.4rem); margin-bottom: -0.1rem; transition-duration: 0.2s; cursor: pointer; @@ -4038,6 +4210,16 @@ screen and (max-width: 480px) { border-color: var(--color-infinity); } +.o-autobuyer-btn--unavailable { + background: var(--color-disabled); + cursor: auto; +} + +.o-autobuyer-btn--unavailable:hover { + color: black; + background-color: var(--color-disabled); +} + .t-dark .o-autobuyer-btn, .t-s6 .o-autobuyer-btn, .t-s10 .o-autobuyer-btn { @@ -4046,8 +4228,8 @@ screen and (max-width: 480px) { .t-dark .o-autobuyer-btn { color: black; + background: #455a64; border-color: black; - background: #455A64; } .t-dark .o-autobuyer-btn:hover { @@ -4061,10 +4243,6 @@ screen and (max-width: 480px) { border-color: var(--color-infinity); } -.s-base--metro .o-autobuyer-btn { - border-radius: 0; -} - .t-metro .o-autobuyer-btn, .t-inverted-metro .o-autobuyer-btn, .t-s8 .o-autobuyer-btn { @@ -4092,31 +4270,27 @@ screen and (max-width: 480px) { width: 90%; } -/*#endregion o-autobuyer-btn*/ +/* #endregion o-autobuyer-btn */ -/*#region autobuyer-box*/ +/* #region autobuyer-box */ .c-autobuyer-box { width: 23rem; height: 17.5rem; - padding: 1rem; - border: solid 0.1rem grey; - border-radius: 0.4rem; font-size: 1.1rem; -} - -.s-base--metro .c-autobuyer-box { - border-radius: 0; + border: solid 0.1rem grey; + border-radius: var(--var-border-radius, 0.4rem); + padding: 1rem; } .t-s1 .c-autobuyer-box { color: black; - border-color: black; background-color: #dbd242; + border-color: black; } .t-s5 .c-autobuyer-box { - background: #ddd; + background: #dddddd; } .t-s8 .c-autobuyer-box { @@ -4127,24 +4301,35 @@ screen and (max-width: 480px) { font-size: 0.9rem; } +.c-autobuyer-box__mode-select-header { + height: 4rem; +} + .c-autobuyer-box__mode-select { + display: flex; + width: 100%; + min-height: 2rem; + justify-content: center; + align-items: center; + text-align: center; + text-align-last: center; font-family: Typewriter, serif; font-size: 1.2rem; color: black; - text-align: center; - text-align-last: center; - height: 3rem; + border-radius: var(--var-border-radius, 0.4rem); + user-select: none; + cursor: pointer; } .t-metro .c-autobuyer-box__mode-select { color: black; - border: 0.1rem solid #A9A9A9; + border: 0.1rem solid #a9a9a9; } .t-dark .c-autobuyer-box__mode-select, .t-dark-metro .c-autobuyer-box__mode-select { color: black; - background-color: #455A64; + background-color: #455a64; border: 0.1rem solid black; } @@ -4157,7 +4342,7 @@ screen and (max-width: 480px) { .t-s10 .c-autobuyer-box__mode-select { color: white; background-color: black; - border: 0.1rem solid #ccc; + border: 0.1rem solid #cccccc; } .l-autobuyer-box { @@ -4169,20 +4354,19 @@ screen and (max-width: 480px) { .c-autobuyer-box-row { display: flex; + width: 90rem; + position: relative; justify-content: center; align-items: center; - position: relative; color: var(--color-text); - border: .2rem solid #383232; - border-radius: .5rem; - margin: .5rem; - padding: 1rem; - width: 90rem; + border: var(--var-border-width, 0.2rem) solid #383232; + border-radius: var(--var-border-radius, 0.5rem); + margin: 0.5rem; + padding: 1rem 2rem; } .s-base--metro .c-autobuyer-box-row { - border: 0.1rem solid black; - border-radius: 0; + border-color: black; } .t-dark .c-autobuyer-box-row { @@ -4190,19 +4374,18 @@ screen and (max-width: 480px) { } .t-dark-metro .c-autobuyer-box-row { - border: 0.1rem solid var(--color-base); - border-radius: 0; + border-color: var(--color-base); } .t-s1 .c-autobuyer-box-row { - border-color: black; background: var(--color-base); + border-color: black; } .t-s6 .c-autobuyer-box-row, .t-s10 .c-autobuyer-box-row { - border: 0.1rem solid #ccc; background: black; + border: 0.1rem solid #cccccc; } .t-s8 .c-autobuyer-box-row { @@ -4211,35 +4394,30 @@ screen and (max-width: 480px) { .l-autobuyer-singlet-group { display: flex; - flex-wrap: wrap; flex-grow: 1; + flex-wrap: wrap; width: 91rem; } .c-small-autobuyer-input { width: 10rem; height: 2rem; - margin: 0.2rem 0; border-color: var(--color-laitela--accent); - border-radius: 0.5rem; -} - -.s-base--metro .c-small-autobuyer-input { - border-width: 0.1rem; - border-radius: 0; + border-width: var(--var-border-width, 0.2rem); + border-radius: var(--var-border-radius, 0.5rem); + margin: 0.2rem 0; } .c-autobuyer-box-slot { position: relative; - border: .2rem solid #383232; - border-radius: .5rem; - margin: .25rem; - padding: 1rem .5rem; + border: var(--var-border-width, 0.2rem) solid #383232; + border-radius: var(--var-border-radius, 0.5rem); + margin: 0.25rem; + padding: 1rem 0.5rem; } .s-base--metro .c-autobuyer-box-slot { - border: 0.1rem solid black; - border-radius: 0; + border-color: black; } .t-dark .c-autobuyer-box-slot { @@ -4247,31 +4425,32 @@ screen and (max-width: 480px) { } .t-dark-metro .c-autobuyer-box-slot { - border: 0.1rem solid var(--color-base); - border-radius: 0; + border-color: var(--color-base); } .t-s1 .c-autobuyer-box-slot { - border-color: black; background: var(--color-base); + border-color: black; } .t-s6 .c-autobuyer-box-slot, .t-s10 .c-autobuyer-box-slot { - border: 0.1rem solid #ccc; background: black; + border: 0.1rem solid #cccccc; } .t-s8 .c-autobuyer-box-slot { background: white; } +/* stylelint-disable selector-class-pattern */ .c-autobuyer-box-row__intervalSlot, .c-autobuyer-box-row__toggleSlot, .c-autobuyer-box-row__checkboxSlot, .l-autobuyer-box__header { width: 25%; } +/* stylelint-enable selector-class-pattern */ .l-autobuyer-box__title { width: 20rem; @@ -4279,31 +4458,31 @@ screen and (max-width: 480px) { .l-autobuyer-box__autobuyers { display: flex; - flex-wrap: wrap; flex-grow: 1; + flex-wrap: wrap; width: 80rem; } .l-autobuyer-box__autobuyers-internal { display: inline-flex; - align-items: center; - justify-content: center; flex-direction: column; + justify-content: center; + align-items: center; } .l-autobuyer-box__content { - flex: 1 0 auto; display: flex; + flex: 1 0 auto; flex-direction: column; justify-content: space-evenly; align-items: center; } .l-autobuyer-box__footer { + width: 0%; position: absolute; top: -0.2rem; right: 1.8rem; - width: 0%; } .s-base--metro .l-autobuyer-box__footer, @@ -4322,21 +4501,22 @@ screen and (max-width: 480px) { flex-grow: 1; } -/*#endregion autobuyer-box*/ +/* #endregion autobuyer-box */ -/*#endregion Autobuyers*/ +/* #endregion Autobuyers */ -/*#region Replicanti*/ +/* #region Replicanti */ .l-replicanti-tab { display: flex; flex-direction: column; - align-items: center; - /* To prevent button jitter*/ + + /* To prevent button jitter */ width: 100%; + align-items: center; } -/*#region c-replicanti-description*/ +/* #region c-replicanti-description */ .c-replicanti-description { font-size: 1.5rem; @@ -4344,7 +4524,7 @@ screen and (max-width: 480px) { .c-replicanti-description__accent { font-size: 2.3rem; - color: black + color: #2196f3; } .s-base--metro .c-replicanti-description__accent, @@ -4360,7 +4540,7 @@ screen and (max-width: 480px) { .t-metro .c-replicanti-description__accent, .t-s8 .c-replicanti-description__accent { - text-shadow: 0 0 0.1rem rgba(0, 0, 0, 0.5), -0.1rem 0.1rem 0.1rem black; + text-shadow: 0 0 0.1rem rgba(0, 0, 0, 50%), -0.1rem 0.1rem 0.1rem black; } .t-dark .c-replicanti-description__accent, @@ -4369,7 +4549,7 @@ screen and (max-width: 480px) { text-shadow: 0 0 0.7rem #03a9f4; } -/*#endregion c-replicanti-description*/ +/* #endregion c-replicanti-description */ .l-replicanti-upgrade-row { display: flex; @@ -4380,11 +4560,11 @@ screen and (max-width: 480px) { margin: 0.3rem; } -/*#endregion Replicanti*/ +/* #endregion Replicanti */ -/*#region Break Infinity*/ +/* #region Break Infinity */ -/*#region l-break-infinity-tab*/ +/* #region l-break-infinity-tab */ .l-break-infinity-tab { display: flex; @@ -4405,9 +4585,9 @@ screen and (max-width: 480px) { margin-top: 1rem; } -/*#endregion l-break-infinity-tab*/ +/* #endregion l-break-infinity-tab */ -/*#region l-break-infinity-upgrade-grid*/ +/* #region l-break-infinity-upgrade-grid */ .l-break-infinity-upgrade-grid { display: flex; @@ -4423,11 +4603,11 @@ screen and (max-width: 480px) { margin: 0.5rem 0.8rem; } -/*#endregion l-break-infinity-upgrade-grid*/ +/* #endregion l-break-infinity-upgrade-grid */ -/*#endregion Break Infinity*/ +/* #endregion Break Infinity */ -/*#region eternity-tab*/ +/* #region eternity-tab */ .c-eternity-tab__header { font-size: 1.5rem; @@ -4438,9 +4618,9 @@ screen and (max-width: 480px) { color: var(--color-eternity); } -/*#region themes*/ +/* #region themes */ -/*#region t-dark t-s6 t-s10*/ +/* #region t-dark t-s6 t-s10 */ .t-dark .c-eternity-tab__eternity-points, .t-s6 .c-eternity-tab__eternity-points, @@ -4448,21 +4628,21 @@ screen and (max-width: 480px) { text-shadow: 0 0 0.7rem; } -/*#endregion t-dark t-s6 t-s10*/ +/* #endregion t-dark t-s6 t-s10 */ -/*#region t-s1*/ +/* #region t-s1 */ .t-s1 .c-eternity-tab__eternity-points { text-shadow: 0.1rem 0.1rem 0 black; } -/*#endregion t-s1*/ +/* #endregion t-s1 */ -/*#endregion themes*/ +/* #endregion themes */ -/*#endregion eternity-tab*/ +/* #endregion eternity-tab */ -/*#region Eternity Upgrades*/ +/* #region Eternity Upgrades */ .l-eternity-upgrades-grid { display: flex; @@ -4481,41 +4661,23 @@ screen and (max-width: 480px) { } .o-eternity-upgrade { - font-weight: bold; - font-size: 1rem; - border: 0.1rem solid var(--color-eternity); width: 20rem; height: 9rem; + font-family: Typewriter, serif; + font-size: 1rem; + font-weight: bold; + border: 0.1rem solid var(--color-eternity); + border-radius: var(--var-border-radius, 0.4rem); transition-duration: 0.2s; cursor: pointer; - border-radius: 0.4rem; - font-family: Typewriter, serif; } -.o-eternity-upgrade--useless-available { - background-color: var(--color-pelle--base); +.o-eternity-upgrade--useless { color: black; - border-color: black; -} - -.o-eternity-upgrade--useless-available:hover { - background-color: var(--color-pelle-secondary); - color:black; -} - -.o-eternity-upgrade--useless-unavailable { background-color: var(--color-pelle--base); - color: black; - filter: grayscale(90%); border-color: black; } -.o-eternity-upgrade--useless-unavailable:hover { - background-color: var(--color-bad); - filter: grayscale(0%); - cursor:default -} - .o-eternity-upgrade--available { color: var(--color-eternity); background-color: black; @@ -4543,10 +4705,6 @@ screen and (max-width: 480px) { cursor: default; } -.s-base--metro .o-eternity-upgrade { - border-radius: 0; -} - .t-metro .o-eternity-upgrade--available { box-shadow: 0.1rem 0.1rem 0.1rem 0 #9e9e9e; } @@ -4635,9 +4793,9 @@ screen and (max-width: 480px) { background: #636363; } -/*#endregion Eternity Upgrades*/ +/* #endregion Eternity Upgrades */ -/*#region Eternity Milestones*/ +/* #region Eternity Milestones */ .l-eternity-milestone-grid { display: flex; @@ -4655,20 +4813,20 @@ screen and (max-width: 480px) { } .o-eternity-milestone__goal { - font-size: 2rem; text-align: left; + font-size: 2rem; } .o-eternity-milestone__reward { width: 25rem; height: 8rem; - color: black; - font-weight: bold; - border: 0.1rem solid var(--color-eternity); - transition-duration: 0.2s; - border-radius: 0.4rem; font-family: Typewriter, serif; font-size: 1.2rem; + font-weight: bold; + color: black; + border: 0.1rem solid var(--color-eternity); + border-radius: var(--var-border-radius, 0.4rem); + transition-duration: 0.2s; } .o-eternity-milestone__reward--locked { @@ -4684,15 +4842,11 @@ screen and (max-width: 480px) { font-size: 1.1rem; } -.s-base--metro .o-eternity-milestone__reward { - border-radius: 0; -} - .s-base--metro .o-eternity-milestone__reward--locked, .t-s1 .o-eternity-milestone__reward--locked { background-color: #9e9e9e; - box-shadow: 0.1rem 0.1rem 0.1rem 0 black; border: none; + box-shadow: 0.1rem 0.1rem 0.1rem 0 black; } .t-dark .o-eternity-milestone__reward--locked, @@ -4709,9 +4863,9 @@ screen and (max-width: 480px) { flex-direction: column; } -/*#endregion Eternity Milestones*/ +/* #endregion Eternity Milestones */ -/*#region Dilation*/ +/* #region Dilation */ .l-dilation-tab { display: flex; @@ -4719,31 +4873,31 @@ screen and (max-width: 480px) { align-items: center; } -/*#region c-dilation-tab*/ +/* #region c-dilation-tab */ .c-dilation-tab__tachyons { - color: black; font-size: 3.5rem; + color: black; } .c-dilation-tab__dilated-time { - color: black; font-size: 3.5rem; + color: black; } .c-dilation-tab__dilated-time-income { - color: black; font-size: 2.5rem; + color: black; } .c-dilation-tab__galaxy-threshold { - color: black; font-size: 2.5rem; + color: black; } .c-dilation-tab__galaxies { - color: black; font-size: 2.5rem; + color: black; } .t-metro .c-dilation-tab__tachyons, @@ -4753,8 +4907,8 @@ screen and (max-width: 480px) { } .t-dark .c-dilation-tab__tachyons, -.t-s6 .c-dilation-tab__tachyons, -.t-s10 .c-dilation-tab__tachyons { +.t-s6 .c-dilation-tab__tachyons, +.t-s10 .c-dilation-tab__tachyons { text-shadow: 0 0 0.7rem #64ddad; } @@ -4768,7 +4922,7 @@ screen and (max-width: 480px) { .t-s8 .c-dilation-tab__dilated-time-income, .t-s8 .c-dilation-tab__galaxy-threshold, .t-s8 .c-dilation-tab__galaxies { - text-shadow: 0 0 0.1rem rgba(0, 0, 0, 0.5), -0.1rem 0.1rem 0.1rem black; + text-shadow: 0 0 0.1rem rgba(0, 0, 0, 50%), -0.1rem 0.1rem 0.1rem black; } .t-metro .c-dilation-tab__dilated-time, @@ -4801,9 +4955,9 @@ screen and (max-width: 480px) { text-shadow: 0 0 0.7rem var(--color-dilation); } -/*#endregion c-dilation-tab*/ +/* #endregion c-dilation-tab */ -/*#region l-dilation-upgrades-grid*/ +/* #region l-dilation-upgrades-grid */ .l-dilation-upgrades-grid { display: flex; @@ -4820,20 +4974,20 @@ screen and (max-width: 480px) { margin: 1.2rem 1.5rem; } -/*#endregion l-dilation-upgrades-grid*/ +/* #endregion l-dilation-upgrades-grid */ -/*#region o-dilation-btn*/ +/* #region o-dilation-btn */ .o-dilation-btn { - font-weight: bold; - font-size: 1.12rem; - border: 0.2rem solid var(--color-dilation); width: 19rem; height: 9rem; - transition-duration: 0.2s; - border-radius: 0.4rem; font-family: Typewriter, serif; + font-size: 1.12rem; + font-weight: bold; + border: var(--var-border-width, 0.2rem) solid var(--color-dilation); + border-radius: var(--var-border-radius, 0.4rem); padding: 1rem; + transition-duration: 0.2s; } .o-dilation-btn--locked { @@ -4845,8 +4999,8 @@ screen and (max-width: 480px) { .o-dilation-btn--unlocked { color: var(--color-dilation); background-color: black; - cursor: pointer; animation: a-dilation-btn-glow 10s infinite; + cursor: pointer; } .o-dilation-btn--unlocked:hover { @@ -4869,11 +5023,6 @@ screen and (max-width: 480px) { animation: a-dilation-btn-glow--cancer 10s infinite; } -.s-base--metro .o-dilation-btn { - border-radius: 0; - border-width: 0.1rem; -} - .o-dilation-btn--locked:hover { color: #1d1d1d; background-color: #660000; @@ -4883,8 +5032,8 @@ screen and (max-width: 480px) { .t-s1 .o-dilation-btn--locked { color: black; background-color: #9e9e9e; - box-shadow: .1rem .1rem .1rem 0 black; border: none; + box-shadow: 0.1rem 0.1rem 0.1rem 0 black; } .s-base--metro .o-dilation-btn--locked:hover { @@ -4910,74 +5059,56 @@ screen and (max-width: 480px) { .t-s6 .o-dilation-btn--locked:hover, .t-s10 .o-dilation-btn--locked:hover { color: black; - border-color: #b84b5f; background-color: #b84b5f; + border-color: #b84b5f; } @keyframes a-dilation-btn-glow { - 0% { box-shadow: inset 0.5rem 0 2rem } - 25% { box-shadow: inset 0 0.5rem 2rem } - 50% { box-shadow: inset -0.5rem 0 2rem } - 75% { box-shadow: inset 0 -0.5rem 2rem } - 100% { box-shadow: inset 0.5rem 0 2rem } + 0% { box-shadow: inset 0.5rem 0 2rem; } + 25% { box-shadow: inset 0 0.5rem 2rem; } + 50% { box-shadow: inset -0.5rem 0 2rem; } + 75% { box-shadow: inset 0 -0.5rem 2rem; } + 100% { box-shadow: inset 0.5rem 0 2rem; } } @keyframes a-dilation-btn-glow--cancer { - 0% { box-shadow: inset 0.5px -0.5rem 2rem } - 10% { box-shadow: inset -0.5px 0.5rem 2rem } - 20% { box-shadow: inset 0.5px -0.5rem 2rem } - 22% { box-shadow: inset -0.5px 0.5rem 2rem } - 25% { box-shadow: inset 0.5px -0.5rem 2rem } - 29% { box-shadow: inset -0.5px 0.5rem 2rem } - 39% { box-shadow: inset 0.5px -0.5rem 2rem } - 44% { box-shadow: inset -0.5px 0.5rem 2rem } - 53% { box-shadow: inset 0.5px -0.5rem 2rem } - 57% { box-shadow: inset -0.5px 0.5rem 2rem } - 63% { box-shadow: inset 0.5px -0.5rem 2rem } - 69% { box-shadow: inset -0.5px 0.5rem 2rem } - 71% { box-shadow: inset 0.5px -0.5rem 2rem } - 74% { box-shadow: inset -0.5px 0.5rem 2rem } - 75% { box-shadow: inset 0.5px -0.5rem 2rem } - 84% { box-shadow: inset -0.5px 0.5rem 2rem } - 88% { box-shadow: inset 0.5px -0.5rem 2rem } - 92% { box-shadow: inset -0.5px 0.5rem 2rem } - 93% { box-shadow: inset 0.5px -0.5rem 2rem } - 95% { box-shadow: inset -0.5px 0.5rem 2rem } - 100% { box-shadow: inset 0.5px -0.5rem 2rem } + 0% { box-shadow: inset 0.5px -0.5rem 2rem; } + 10% { box-shadow: inset -0.5px 0.5rem 2rem; } + 20% { box-shadow: inset 0.5px -0.5rem 2rem; } + 22% { box-shadow: inset -0.5px 0.5rem 2rem; } + 25% { box-shadow: inset 0.5px -0.5rem 2rem; } + 29% { box-shadow: inset -0.5px 0.5rem 2rem; } + 39% { box-shadow: inset 0.5px -0.5rem 2rem; } + 44% { box-shadow: inset -0.5px 0.5rem 2rem; } + 53% { box-shadow: inset 0.5px -0.5rem 2rem; } + 57% { box-shadow: inset -0.5px 0.5rem 2rem; } + 63% { box-shadow: inset 0.5px -0.5rem 2rem; } + 69% { box-shadow: inset -0.5px 0.5rem 2rem; } + 71% { box-shadow: inset 0.5px -0.5rem 2rem; } + 74% { box-shadow: inset -0.5px 0.5rem 2rem; } + 75% { box-shadow: inset 0.5px -0.5rem 2rem; } + 84% { box-shadow: inset -0.5px 0.5rem 2rem; } + 88% { box-shadow: inset 0.5px -0.5rem 2rem; } + 92% { box-shadow: inset -0.5px 0.5rem 2rem; } + 93% { box-shadow: inset 0.5px -0.5rem 2rem; } + 95% { box-shadow: inset -0.5px 0.5rem 2rem; } + 100% { box-shadow: inset 0.5px -0.5rem 2rem; } } -/*#endregion o-dilation-btn*/ +/* #endregion o-dilation-btn */ -/*#region o-dilation-upgrade*/ +/* #region o-dilation-upgrade */ .o-dilation-upgrade { - background: black; - font-weight: bold; - font-size: 1.05rem; - border: 0.1rem solid; - width: 17rem; - height: 9.5rem; - transition-duration: 0.2s; - border-radius: 0.4rem; + width: 19rem; + height: 10rem; font-family: Typewriter, serif; -} - -.o-dilation-upgrade--useless-available { - color: var(--color-pelle--base); - border-color: var(--color-dilation); - animation: a-dilation-btn-glow 10s infinite; - cursor: pointer; -} - -.o-dilation-upgrade--useless-available:hover { - color: var(--color-pelle-secondary); -} - -.o-dilation-upgrade--useless-bought { - color: black; - background-color: var(--color-pelle--base) !important; - filter: grayscale(50%); - border-color: black; + font-size: 1.05rem; + font-weight: bold; + background: black; + border: 0.1rem solid; + border-radius: var(--var-border-radius, 0.4rem); + transition-duration: 0.2s; } .o-dilation-upgrade--available { @@ -4996,23 +5127,16 @@ screen and (max-width: 480px) { background-color: white; } -.o-dilation-upgrade--bought, .o-dilation-upgrade--capped { +.o-dilation-upgrade--bought, +.o-dilation-upgrade--capped { color: black; background-color: var(--color-dilation); border-color: black; } -.o-dilation-upgrade--useless-unavailable { - background-color: var(--color-pelle--base) !important; +.o-dilation-upgrade--useless { color: black; - filter: grayscale(90%); - border-color: black; -} - -.o-dilation-upgrade--useless-unavailable:hover { - background-color: var(--color-bad); - filter: grayscale(0%); - cursor:default + background-color: var(--color-pelle--base); } .o-dilation-upgrade--unavailable { @@ -5034,16 +5158,12 @@ screen and (max-width: 480px) { font-size: 0.95rem; } -.s-base--metro .o-dilation-upgrade { - border-radius: 0; -} - .s-base--metro .o-dilation-upgrade--unavailable, .t-s1 .o-dilation-upgrade--unavailable { color: black; background-color: #9e9e9e; - box-shadow: 0.1rem 0.1rem 0.1rem 0 black; border: none; + box-shadow: 0.1rem 0.1rem 0.1rem 0 black; } .s-base--metro .o-dilation-upgrade--unavailable:hover { @@ -5071,7 +5191,8 @@ screen and (max-width: 480px) { color: #64ddad; } -.t-dark .o-dilation-upgrade--bought, .t-dark .o-dilation-upgrade--capped { +.t-dark .o-dilation-upgrade--bought, +.t-dark .o-dilation-upgrade--capped { background-color: var(--color-dilation); } @@ -5090,8 +5211,8 @@ screen and (max-width: 480px) { .t-s6 .o-dilation-upgrade--unavailable:hover, .t-s10 .o-dilation-upgrade--unavailable:hover { color: black; - border-color: var(--color-bad); background-color: var(--color-bad); + border-color: var(--color-bad); } .t-s4 .o-dilation-upgrade--available { @@ -5105,16 +5226,14 @@ screen and (max-width: 480px) { background: var(--color-dilation); } -/*#endregion o-dilation-upgrade*/ - - +/* #endregion o-dilation-upgrade */ .c-tachyon-particle-container { - position: fixed; - top: 0; - left: 0; width: 100%; height: 100%; + position: absolute; + top: 0; + left: 0; z-index: -1; } @@ -5126,54 +5245,60 @@ screen and (max-width: 480px) { fill: white; } -/*#endregion Dilation*/ +/* #endregion Dilation */ -/*#region Modals*/ +/* #region Modals */ @keyframes a-modal-overlay-fadein { - from {background-color: rgba(0,0,0,0); } - to {background-color: rgba(60, 60, 100, 0.5);} + from { background-color: rgba(0, 0, 0, 0%); } + to { background-color: rgba(60, 60, 100, 50%); } } .l-modal-overlay { - position: fixed; - left: 0; - top: 0; + /* stylelint-disable-next-line unit-allowed-list */ width: 100vw; + /* stylelint-disable-next-line unit-allowed-list */ height: 100vh; + position: absolute; + top: 0; + left: 0; z-index: 6; animation-name: a-modal-overlay-fadein; animation-duration: 2s; animation-fill-mode: forwards; + pointer-events: auto; } .l-modal { - position: fixed; + display: inline-block; + position: absolute; top: 50%; left: 50%; - transform: translate(-50%, -50%); z-index: 7; - display: inline-block; + transform: translate(-50%, -50%); + pointer-events: auto; } -/*#region c-modal*/ +/* #region c-modal */ .c-modal { - color: var(--color-text); - background-color: var(--color-base); - border: 0.3rem solid black; - border-radius: 0.6rem; text-align: center; font-family: Typewriter, serif; font-size: 1.4rem; + color: var(--color-text); + background-color: var(--color-base); + border: var(--var-border-width, 0.3rem) solid black; + border-radius: var(--var-border-radius, 0.6rem); padding: 1rem; transition-duration: 0.2s; } .c-modal__title { + display: inline-block; width: 50rem; font-size: 1.6rem; font-weight: bold; + padding: 0 3rem; } .c-modal__close-btn { @@ -5183,9 +5308,9 @@ screen and (max-width: 480px) { } .c-modal__close-btn--tiny { - transform: scale(0.5); top: -0.5rem; right: -0.5rem; + transform: scale(0.5); } .l-modal-buttons { @@ -5195,71 +5320,61 @@ screen and (max-width: 480px) { } .c-modal__confirmation-toggle { - display: flex; - justify-content: center; - align-items: center; - background: var(--color-gh-purple); - font-size: 1.9rem; - height: 3rem; - width: 3rem; - border: 0.3rem solid black; - border-top-right-radius: 0.3rem; - border-bottom-left-radius: 1rem; - color: black; cursor: pointer; - user-select: none; - transition-duration: 0.2s; - position: absolute; - top: -0.3rem; - right: -0.3rem; } -.c-modal__confirmation-toggle:hover { - transform: scale(1.1) translate(-0.15rem, 0.15rem); +.c-modal__confirmation-toggle__checkbox { + display: inline-flex; + width: 2rem; + height: 2rem; + position: relative; + justify-content: center; + align-items: center; + vertical-align: middle; + font-size: 1.3rem; + color: black; + background: var(--color-gh-purple); + border: var(--var-border-width, 0.3rem) solid black; + border-radius: var(--var-border-radius, 0.3rem); + transition-duration: 0.2s; + -webkit-user-select: none; + user-select: none; } .s-base--metro .c-modal__confirmation-toggle { - border-radius: 0; - border-width: 0.1rem; top: -0.1rem; right: -0.1rem; } -.t-s6 .c-modal__confirmation-toggle, -.t-s10 .c-modal__confirmation-toggle { +.t-s6 .c-modal__confirmation-toggle__checkbox, +.t-s10 .c-modal__confirmation-toggle__checkbox { + top: -0.1rem; + right: -0.1rem; border: 0.1rem solid #1b5e20; - top: -0.1rem; - right: -0.1rem; } -.t-s11 .c-modal__confirmation-toggle { - border-color: #e1ae18; - top: -0.2rem; -} - -.c-modal__confirmation-toggle--active { +.c-modal__confirmation-toggle__checkbox--active { background: var(--color-good); } -.c-modal__confirmation-toggle__tooltip { - opacity: 0; - transition-duration: 0.2s; - font-size: 1.4rem; - color: var(--color-text); - background-color: var(--color-base); - border: 0.3rem solid black; - border-radius: 0.8rem; - min-width: 20rem; - margin-left: 0; - padding: 0.4rem; - pointer-events: none; - position: absolute; - bottom: 100%; +.c-modal__confirmation-toggle:hover .c-modal__confirmation-toggle__checkbox { + transform: scale(1.1); } -.s-base--metro .c-modal__confirmation-toggle__tooltip { - border-radius: 0; - border-width: 0.1rem; +.c-modal__confirmation-toggle__tooltip { + min-width: 20rem; + position: absolute; + bottom: 100%; + font-size: 1.4rem; + opacity: 0; + color: var(--color-text); + background-color: var(--color-base); + border: var(--var-border-width, 0.3rem) solid black; + border-radius: var(--var-border-radius, 0.8rem); + margin-left: 0; + padding: 0.4rem; + transition-duration: 0.2s; + pointer-events: none; } .t-s6 .c-modal__confirmation-toggle__tooltip, @@ -5267,39 +5382,44 @@ screen and (max-width: 480px) { border: 0.1rem solid #1b5e20; } -.c-modal__confirmation-toggle__tooltip:after { +.c-modal__confirmation-toggle__tooltip::after { + content: " "; + width: 0; position: absolute; bottom: 0; left: 50%; - margin-left: -0.7rem; - margin-bottom: 0; - width: 0; + z-index: 0; border-top: 0 solid black; border-right: 0.7rem solid transparent; border-left: 0.7rem solid transparent; - content: " "; + margin-bottom: 0; + margin-left: -0.7rem; transition-duration: 0.2s; - z-index: 0; } .c-modal__confirmation-toggle:hover .c-modal__confirmation-toggle__tooltip { - opacity: 1; bottom: calc(100% + 0.8rem); + opacity: 1; } .s-base--metro .c-modal__confirmation-toggle:hover .c-modal__confirmation-toggle__tooltip { bottom: calc(100% + 0.7rem); } -.c-modal__confirmation-toggle:hover .c-modal__confirmation-toggle__tooltip:after { +.c-modal__confirmation-toggle:hover .c-modal__confirmation-toggle__tooltip::after { border-top-width: 0.6rem; margin-bottom: -0.8rem; } -.s-base--metro .c-modal__confirmation-toggle:hover .c-modal__confirmation-toggle__tooltip:after { +.s-base--metro .c-modal__confirmation-toggle:hover .c-modal__confirmation-toggle__tooltip::after { margin-bottom: -0.7rem; } +.c-modal__confirmation-toggle__text { + vertical-align: middle; + opacity: 0.7; +} + .c-modal__confirm-btn { background-color: var(--color-good) !important; } @@ -5308,9 +5428,9 @@ screen and (max-width: 480px) { background-color: var(--color-good-dark) !important; } -/*#region themes*/ +/* #region themes */ -/*#region t-dark t-dark-metro*/ +/* #region t-dark t-dark-metro */ .t-dark .c-modal { box-shadow: 0 0 1.5rem 0 black; @@ -5322,22 +5442,13 @@ screen and (max-width: 480px) { .t-dark h3, .t-dark-metro h3 { - color: #999; - text-shadow: -0.1rem 0.1rem 0 rgba(0, 0, 0, 0.5); + color: #999999; + text-shadow: -0.1rem 0.1rem 0 rgba(0, 0, 0, 50%); } -/*#endregion t-dark t-dark-metro*/ +/* #endregion t-dark t-dark-metro */ -/*#region t-metro t-dark-metro t-inverted-metro t-s8 */ - -.s-base--metro .c-modal { - border-width: 0.1rem; - border-radius: 0; -} - -/*#endregion t-metro t-dark-metro t-inverted-metro t-s8 */ - -/*#region t-s6 t-s10*/ +/* #region t-s6 t-s10 */ .t-s6 .c-modal, .t-s10 .c-modal { @@ -5345,27 +5456,27 @@ screen and (max-width: 480px) { box-shadow: 0 0 1.5rem 0 black; } -/*#endregion t-s6 t-s10*/ +/* #endregion t-s6 t-s10 */ -/*#region t-s7*/ +/* #region t-s7 */ .t-s7 .c-modal { filter: saturate(0); } -/*#endregion t-s7*/ +/* #endregion t-s7 */ -/*#region t-s8*/ +/* #region t-s8 */ .t-s8 h3 { color: black; } -/*#endregion t-s8*/ +/* #endregion t-s8 */ -/*#endregion themes*/ +/* #endregion themes */ -/*#endregion c-modal*/ +/* #endregion c-modal */ .l-modal-content--centered { display: flex; @@ -5374,7 +5485,7 @@ screen and (max-width: 480px) { align-items: center; } -/*#region c-modal-message*/ +/* #region c-modal-message */ .c-modal-message { min-width: 50rem; @@ -5388,13 +5499,13 @@ screen and (max-width: 480px) { margin: 1rem 0.5rem 0; } -/*#endregion c-modal-message*/ +/* #endregion c-modal-message */ -/*#region c-modal-hard-reset*/ +/* #region c-modal-hard-reset */ .c-modal-hard-reset-danger { - color: var(--color-bad); font-weight: bold; + color: var(--color-bad); } .c-modal-hard-reset-btn { @@ -5409,23 +5520,24 @@ screen and (max-width: 480px) { width: 45rem; } -/*#endregion c-modal-hard-reset*/ +/* #endregion c-modal-hard-reset */ -/*#region c-modal-away-progress*/ +/* #region c-modal-away-progress */ .c-modal-away-progress { display: flex; flex-direction: column; - align-items: center; min-width: 40rem; + align-items: center; + user-select: none; } .t-dark .c-modal-away-progress { text-shadow: - 0 0 0.2rem black, - 0 0 0.2rem black, - 0 0 0.2rem black, - 0 0 0.2rem black; + 0 0 0.2rem black, + 0 0 0.2rem black, + 0 0 0.2rem black, + 0 0 0.2rem black; } .c-modal-away-progress__header { @@ -5436,9 +5548,9 @@ screen and (max-width: 480px) { } .c-modal-away-progress__resources div { + border-bottom: 0.1rem solid var(--color-text); margin-bottom: 0.2rem; padding-bottom: 0.2rem; - border-bottom: 0.1rem solid var(--color-text); cursor: pointer; } @@ -5458,7 +5570,7 @@ screen and (max-width: 480px) { .c-modal-away-progress__dimension-boosts, .c-modal-away-progress__antimatter-galaxies, .c-modal-away-progress__antimatter { - color: var(--color-antimatter) + color: var(--color-antimatter); } .t-dark .c-modal-away-progress__antimatter, @@ -5481,13 +5593,13 @@ screen and (max-width: 480px) { .c-modal-away-progress__tachyon-galaxies, .c-modal-away-progress__dilated-time { color: var(--color-dilation); - filter: brightness(0.8) + filter: brightness(0.8); } .t-dark .c-modal-away-progress__tachyon-particles, .t-dark .c-modal-away-progress__tachyon-galaxies, .t-dark .c-modal-away-progress__dilated-time { - filter: none + filter: none; } .c-modal-away-progress__realities, @@ -5517,20 +5629,20 @@ screen and (max-width: 480px) { } .c-modal-away-progress__teresa-memories { - color: var(--color-ra-pet-teresa); + color: var(--color-ra-pet--teresa); } .c-modal-away-progress__relic-shards, .c-modal-away-progress__effarig-memories { - color: var(--color-ra-pet-effarig); + color: var(--color-ra-pet--effarig); } .c-modal-away-progress__enslaved-memories { - color: var(--color-ra-pet-enslaved); + color: var(--color-ra-pet--enslaved); } .c-modal-away-progress__v-memories { - color: var(--color-ra-pet-v); + color: var(--color-ra-pet--v); } .c-modal-away-progress__teresa-memories, @@ -5544,15 +5656,23 @@ screen and (max-width: 480px) { .t-dark .c-modal-away-progress__effarig-memories, .t-dark .c-modal-away-progress__enslaved-memories, .t-dark .c-modal-away-progress__v-memories { - filter: none + filter: none; } .c-modal-away-progress__black-hole b, .c-modal-away-progress__black-hole { color: black; text-shadow: - 0 0 0.2rem #e67919, - 0 0 0.3rem #e67919; + 0 0 0.2rem #e67919, + 0 0 0.3rem #e67919; +} + +.s-base--dark .c-modal-away-progress__black-hole b, +.s-base--dark .c-modal-away-progress__black-hole { + color: #de5a1d; + text-shadow: + 0 0 0.2rem black, + 0 0 0.3rem black; } .c-modal-away-progress__reality-shards { @@ -5561,16 +5681,16 @@ screen and (max-width: 480px) { .c-modal-away-progress__disabled b, .c-modal-away-progress__disabled { - text-decoration: line-through; - color: #303030; font-style: italic; + color: #303030; text-shadow: 0 0 0.3rem #303030; + text-decoration: line-through; animation: none; } -/*#endregion c-modal-away-progress*/ +/* #endregion c-modal-away-progress */ -/*#region c-modal-import*/ +/* #region c-modal-import */ .c-modal-import__save-info > div { margin-top: 0.2rem; @@ -5584,13 +5704,19 @@ screen and (max-width: 480px) { width: 45rem; } -/*#endregion c-modal-import*/ +.c-modal-IAP__warning { + font-size: 1.6rem; + font-weight: bold; + color: #ff6666; +} -/*#region c-modal-import-tree*/ +/* #endregion c-modal-import */ + +/* #region c-modal-import-tree */ .c-modal-import-tree { - width: 48rem; word-break: break-word; + width: 48rem; } .l-modal-import-tree__tree-info-line { @@ -5609,13 +5735,14 @@ screen and (max-width: 480px) { margin-top: 0.3rem; } -/*#endregion c-modal-import-tree*/ +/* #endregion c-modal-import-tree */ .c-modal-input { - border: 0.1rem solid black; - background-color: #F2F2F2; - border-radius: 0.3rem; text-align: center; + background-color: #f2f2f2; + border: 0.1rem solid black; + border-radius: var(--var-border-radius, 0.3rem); + margin: 0.5rem; } .c-modal-options { @@ -5626,9 +5753,10 @@ screen and (max-width: 480px) { width: 50rem; } -.c-modal-options .o-primary-btn--option, .o-primary-btn--option-wide { - padding: 0.5rem; +.c-modal-options .o-primary-btn--option, +.o-primary-btn--option-wide { margin: 0.5rem; + padding: 0.5rem; } .c-modal-options .o-primary-btn--width-medium { @@ -5641,13 +5769,13 @@ screen and (max-width: 480px) { justify-content: center; } -/*#region l-modal-options*/ +/* #region l-modal-options */ .l-modal-options { display: flex; flex-direction: column; - align-items: center; justify-content: space-evenly; + align-items: center; } .l-modal-options__save-record { @@ -5657,7 +5785,7 @@ screen and (max-width: 480px) { margin-bottom: 1rem; } -/*#endregion l-modal-options*/ +/* #endregion l-modal-options */ .l-modal-store-content { display: flex; @@ -5676,31 +5804,32 @@ screen and (max-width: 480px) { .c-modal-store-btn-container { display: flex; + justify-content: flex-end; align-items: center; margin-bottom: 1.5rem; } .o-modal-store-label { - margin-right: 1.5rem; - font-family: Typewriter; - font-size: 2rem; min-width: 10rem; text-align: right; + font-family: Typewriter; + font-size: 2rem; + margin-right: 1.5rem; } .o-modal-store-btn { - padding: 0.8rem; - border: none; - background: burlywood; - border-radius: .5rem; + display: flex; + min-width: 12rem; + justify-content: center; + align-items: center; font-family: Typewriter; font-size: 2rem; - cursor: pointer; + background: burlywood; + border: none; + border-radius: var(--var-border-radius, 0.5rem); + padding: 0.8rem; transition-duration: 0.15s; - display: flex; - align-items: center; - justify-content: center; - min-width: 12rem; + cursor: pointer; } .o-modal-store-btn:hover { @@ -5712,7 +5841,7 @@ screen and (max-width: 480px) { margin-left: 1rem; } -/*#region c-modal-hotkeys*/ +/* #region c-modal-hotkeys */ .c-modal-hotkeys { font-size: 1.25rem; @@ -5724,9 +5853,9 @@ screen and (max-width: 480px) { } .l-modal-hotkeys__column { - width: 25rem; display: flex; flex-direction: column; + width: 25rem; } .l-modal-hotkeys__column--right { @@ -5754,28 +5883,29 @@ screen and (max-width: 480px) { } kbd { + display: inline-block; + vertical-align: middle; + font: 1.1rem SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; + line-height: 1rem; + color: #444d56; background-color: #fafbfc; border: 0.1rem solid #d1d5da; border-bottom-color: #c6cbd1; - border-radius: 0.3rem; + border-radius: var(--var-border-radius, 0.3rem); box-shadow: inset 0 -0.1rem 0 #c6cbd1; - color: #444d56; - display: inline-block; - font: 1.1rem SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace; - line-height: 1rem; padding: 0.3rem 0.5rem; - vertical-align: middle; } -.t-dark kbd, .t-dark-metro kbd { +.t-dark kbd, +.t-dark-metro kbd { + color: #a9b3bc; background-color: #212b36; border-color: #464e58; border-bottom-color: #4f5863; box-shadow: inset 0 -0.1rem 0 #4f5863; - color: #a9b3bc; } -/*#endregion c-modal-hotkeys*/ +/* #endregion c-modal-hotkeys */ .l-modal-split-preferences { display: flex; @@ -5783,137 +5913,33 @@ kbd { align-items: center; } -.l-modal-celestial-quote { - position: fixed; - z-index: 3; - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - left: 50vw; - top: 50vh; - width: 30rem; - height: 30rem; - transform: translate(-50%, -50%); -} - -.c-modal-cestial-quote__symbol { - position: absolute; - display: flex; - justify-content: center; - align-items: center; - left: 0; - font-size: 25rem; - opacity: 0.2; - text-shadow: 0 0 2rem; - width: 100%; - height: 100%; - pointer-events: none; -} - -.c-modal-celestial-quote--teresa { - color: var(--color-teresa--base); - background-color: black; - border-color: var(--color-teresa--base); - box-shadow: 0 0 1rem var(--color-teresa--base), 0 0 1rem var(--color-teresa--base) inset; -} - -.c-modal-celestial-quote--effarig { - color: var(--color-effarig--base); - background-color: black; - border-color: var(--color-effarig--base); - box-shadow: 0 0 1rem var(--color-effarig--base), 0 0 1rem var(--color-effarig--base) inset; -} - -.c-modal-celestial-quote--enslaved { - color: var(--color-enslaved-base); - background-color: black; - border-color: var(--color-enslaved-base); - box-shadow: 0 0 1rem var(--color-enslaved-base), 0 0 1rem var(--color-enslaved-base) inset; -} - -.c-modal-celestial-quote--v { - color: var(--color-v--base); - background-color: black; - border-color: var(--color-v--base); - box-shadow: 0 0 1rem var(--color-v--base), 0 0 1rem var(--color-v--base) inset; -} - -.c-modal-celestial-quote--ra { - color: var(--color-ra-base); - background-color: black; - border-color: var(--color-ra-base); - box-shadow: 0 0 1rem var(--color-ra-base), 0 0 1rem var(--color-ra-base) inset; -} - -.c-modal-celestial-quote--laitela { - color: var(--color-laitela--accent); - background-color: var(--color-laitela--base); - border-color: var(--color-laitela--accent); - box-shadow: 0 0 1rem var(--color-laitela--accent), 0 0 1rem var(--color-laitela--accent) inset; -} - -.c-modal-celestial-quote--pelle { - color: var(--color-pelle--base); - background-color: black; - border-color: var(--color-pelle--base); - box-shadow: 0 0 1rem var(--color-pelle--base), 0 0 1rem var(--color-pelle--base) inset; -} - -.c-modal-celestial-quote__arrow { - font-size: 150%; - margin: 0.5rem; - cursor: pointer; -} - -.c-modal-celestial-quote__end { - position: absolute; - font-size: 150%; - cursor: pointer; - left: calc(50% - 1rem); - bottom: 1.5rem; -} - -.l-modal-celestial-quote__text { - display: flex; - flex-direction: column; - justify-content: center; - height: 100%; -} - -.l-modal-celestial-quote__buttons { - display: flex; - flex-direction: column; - justify-content: space-between; - height: 100%; -} - -/*#endregion Modals*/ +/* #endregion Modals */ .l-notification-container { display: flex; flex-direction: column-reverse; - align-items: flex-end; - position: fixed; + position: absolute; top: 0; right: 0; + z-index: 1000; + align-items: flex-end; margin-top: 0.5rem; margin-right: 1rem; - z-index: 1000; + pointer-events: auto; } -/*#region o-notification*/ +/* #region o-notification */ .o-notification { - padding: 0.8rem 1.5rem; - margin-top: 0.3rem; + white-space: nowrap; + font-family: Typewriter, serif; + font-size: 1.3rem; + font-weight: bold; color: black; background-color: white; - font-size: 1.3rem; - font-family: TypeWriter, serif; - font-weight: bold; - white-space: nowrap; border: 0.1rem solid; + margin-top: 0.3rem; + padding: 0.8rem 1.5rem; cursor: pointer; } @@ -5932,6 +5958,11 @@ kbd { border-color: #4980cc; } +.o-notification--infinity { + background-color: var(--color-infinity); + border-color: var(--color-infinity); +} + .o-notification--eternity { background-color: var(--color-eternity); border-color: var(--color-eternity); @@ -5978,10 +6009,10 @@ kbd { } @keyframes a-notification--enter { - 0% { transform: translateX(125%) } - 50% { transform: translateX(-2rem) } - 75% { transform: translateX(1rem) } - 100% { transform: translateX(0) } + 0% { transform: translateX(125%); } + 50% { transform: translateX(-2rem); } + 75% { transform: translateX(1rem); } + 100% { transform: translateX(0); } } .a-notification--leave { @@ -5989,55 +6020,30 @@ kbd { transition: transform 0.25s ease-in; } -/*#endregion o-notification*/ - -/*#region component*/ -/*#endregion component*/ - -.o-celestial-quote-history { - display: flex; - flex-direction: row; - align-items: stretch; - justify-content: space-evenly; - margin: 1rem 0; - width: 70rem; -} - -.l-celestial-quote-history__lines { - font-size: 1.9rem; - line-height: 1.6em; - display: flex; - flex-direction: column; - align-items: center; - flex-grow: 1; -} - -.c-celestial-quote-history__line { - font-weight: bold; - font-style: italic; -} - -.l-celestial-quote-history__buttons { - display: flex; - flex-direction: column; - align-items: center; - margin: 1rem 0 0.5rem 1.5rem; -} - -.c-celestial-quote-history__button { +.l-notification-icon { + position: absolute; + top: -0.1rem; + right: -0.1rem; + z-index: 5; font-size: 1.5rem; - margin-bottom: 0.5rem; + color: var(--color-notification); + text-shadow: -0.2rem 0.2rem 0.5rem black; + animation: a-notification-glow 2s infinite; } -.c-celestial-quote-history__button--enabled { - cursor: pointer; +@keyframes a-notification-glow { + 0% { filter: brightness(70%); } + 50% { filter: brightness(100%); } + 100% { filter: brightness(70%); } } -.c-celestial-quote-history__button--disabled { - opacity: 0.6; -} +/* #endregion o-notification */ -/*#region Teresa tab*/ +/* #region component */ + +/* #endregion component */ + +/* #region Teresa tab */ .l-teresa-celestial-tab { display: flex; flex-direction: column; @@ -6062,19 +6068,20 @@ kbd { } .c-rm-store { - border: 0.1rem solid black; - height: 50rem; width: 13rem; - margin: auto; + height: 50rem; position: relative; + border: 0.1rem solid black; + border-radius: var(--var-border-radius, 0.4rem); + margin: auto; } .c-rm-store-inner { + width: 100%; position: absolute; - background: var(--color-teresa--base); bottom: 0; left: 0; - width: 100%; + background: var(--color-teresa--base); } .c-rm-store-inner--light { @@ -6082,83 +6089,105 @@ kbd { } .c-rm-store-label { + width: 100%; position: absolute; bottom: 0; - width: 100%; - color: black; font-weight: bold; + color: black; } -.c-teresa-unlock-description { - color: var(--color-text); - position: absolute; +.c-teresa-unlock-description--hover-area { + display: flex; width: 100%; height: 3rem; - display: flex; + position: absolute; align-items: center; } -.c-teresa-milestone-line { - height: 0.2rem; - background-color: var(--color-teresa--base); - width: 100%; +.c-teresa-unlock-description { + width: 14rem; + font-size: 1rem; + color: var(--color-teresa--base); + background-color: var(--color-teresa--accent); + border: 0.1rem solid var(--color-teresa--base); } -.c-teresa-milestone-line--attained { - background-color: rgba(0, 0, 0, 0.5); +.c-teresa-unlock-description--unlocked { + color: var(--color-teresa--accent); + background-color: var(--color-teresa--base); +} + +.c-teresa-milestone-line { + width: 100%; + height: 0.2rem; + background-color: var(--color-teresa--base); + animation: a-teresa-unfinished-milestone-flash 3s infinite; +} + +@keyframes a-teresa-unfinished-milestone-flash { + 0% { opacity: 0.7; } + 20% { opacity: 0.7; } + 50% { opacity: 0.4; } + 80% { opacity: 0.7; } + 100% { opacity: 0.7; } +} + +.c-teresa-milestone-line--unlocked { + filter: brightness(50%); + animation: none; } .o-teresa-quotes { display: inline-block; - margin: 1.5rem; font-size: 1.9rem; - font-weight: bold; font-style: italic; + font-weight: bold; + margin: 1.5rem; } .o-quote-button { + font-size: 3rem; + font-weight: bold; background: transparent; border: none; - font-weight: bold; - font-size: 3rem; cursor: pointer; } .c-teresa-unlock { width: 20rem; - border-radius: 0.5rem; - padding: 1rem; - margin: 0 10rem 1rem 10rem; - transition-duration: 0.15s; - user-select: none; - background-color: var(--color-teresa--base); font-weight: bold; - border: none; color: var(--color-teresa--accent); + background-color: var(--color-teresa--base); + border: none; + border-radius: var(--var-border-radius, 0.5rem); + margin: 0 10rem 1rem; + padding: 1rem; + transition-duration: 0.15s; + -webkit-user-select: none; + user-select: none; } .c-teresa-run-button { - color: var(--color-teresa--base); - border: 0.2rem solid var(--color-teresa--base); - background-color: var(--color-teresa--accent); font-weight: bold; + color: var(--color-teresa--base); + background-color: var(--color-teresa--accent); + border: var(--var-border-width, 0.2rem) solid var(--color-teresa--base); } .c-teresa-run-button__icon { - color: var(--color-teresa--base); - font-weight: bold; - font-size: 7rem; - text-shadow: 0.1rem 0.1rem 0.5rem; - cursor: pointer; - height: 12rem; width: 12rem; - border-radius: 50%; - background-color: var(--color-teresa--accent); + height: 12rem; align-items: center; + font-size: 7rem; + font-weight: bold; + color: var(--color-teresa--base); + text-shadow: 0.1rem 0.1rem 0.5rem; + background-color: var(--color-teresa--accent); + border: var(--var-border-width, 0.4rem) solid var(--color-teresa--base); + border-radius: 50%; margin: 1.5rem auto; - border: 0.4rem solid var(--color-teresa--base); - animation: a-teresa-run-button__icon--glow 3s infinite; transition-duration: 0.2s; + animation: a-teresa-run-button__icon--glow 3s infinite; } .c-teresa-run-button__icon:hover { @@ -6173,50 +6202,65 @@ kbd { animation: a-teresa-run-button__icon--super-glow 1.2s infinite, a-teresa-run-button__icon--spin 5s infinite; } +.c-celestial-run-button--clickable { + cursor: pointer; +} + @keyframes a-teresa-run-button__icon--glow { - 0% {text-shadow: 0.1rem 0.1rem 0.3rem;} - 50% {text-shadow: 0.1rem 0.1rem 0.8rem;} - 100% {text-shadow: 0.1rem 0.1rem 0.3rem;} + 0% { text-shadow: 0.1rem 0.1rem 0.3rem; } + 50% { text-shadow: 0.1rem 0.1rem 0.8rem; } + 100% { text-shadow: 0.1rem 0.1rem 0.3rem; } } @keyframes a-teresa-run-button__icon--super-glow { - 0% {text-shadow: 0.1rem 0.1rem 0.3rem; box-shadow: 0 0 0.3rem var(--color-teresa--base);} - 50% {text-shadow: 0.1rem 0.1rem 0.9rem; box-shadow: 0 0 1rem var(--color-teresa--base);} - 100% {text-shadow: 0.1rem 0.1rem 0.3rem; box-shadow: 0 0 0.3rem var(--color-teresa--base);} + 0% { + text-shadow: 0.1rem 0.1rem 0.3rem; + box-shadow: 0 0 0.3rem var(--color-teresa--base); + } + + 50% { + text-shadow: 0.1rem 0.1rem 0.9rem; + box-shadow: 0 0 1rem var(--color-teresa--base); + } + + 100% { + text-shadow: 0.1rem 0.1rem 0.3rem; + box-shadow: 0 0 0.3rem var(--color-teresa--base); + } } @keyframes a-teresa-run-button__icon--spin { - 0% {transform: rotate(61deg);} - 10% {transform: rotate(322deg);} - 20% {transform: rotate(235deg);} - 30% {transform: rotate(222deg);} - 40% {transform: rotate(105deg);} - 50% {transform: rotate(33deg);} - 60% {transform: rotate(103deg);} - 70% {transform: rotate(158deg);} - 80% {transform: rotate(41deg);} - 90% {transform: rotate(73deg);} - 100% {transform: rotate(61deg);} + 0% { transform: rotate(61deg); } + 10% { transform: rotate(322deg); } + 20% { transform: rotate(235deg); } + 30% { transform: rotate(222deg); } + 40% { transform: rotate(105deg); } + 50% { transform: rotate(33deg); } + 60% { transform: rotate(103deg); } + 70% { transform: rotate(158deg); } + 80% { transform: rotate(41deg); } + 90% { transform: rotate(73deg); } + 100% { transform: rotate(61deg); } } .c-teresa-shop { display: flex; flex-direction: column; width: 20rem; - margin-left: 18rem; margin-right: 2rem; + margin-left: 18rem; } .o-teresa-shop-button { - color: black; - background-color: var(--color-disabled); - padding: 1rem; - border: 0.2rem solid var(--color-teresa--base); - border-radius: 0.5rem; - margin-bottom: 1rem; font-family: Typewriter, serif; font-size: 1rem; font-weight: bold; + color: black; + background-color: var(--color-disabled); + border: var(--var-border-width, 0.2rem) solid var(--color-teresa--base); + border-radius: var(--var-border-radius, 0.5rem); + margin-bottom: 1rem; + padding: 1rem; transition-duration: 0.15s; } @@ -6237,10 +6281,10 @@ kbd { } .c-teresa-pour { - font-size: 2rem; width: 13rem; + font-size: 2rem; + margin-bottom: 1rem; padding: 0.5rem; - margin-bottom: 1rem } .c-teresa-pour--unlock-available { @@ -6248,50 +6292,48 @@ kbd { } @keyframes a-teresa-pour--unlock-available { - 0% {box-shadow: 0 0 0.3rem var(--color-teresa--base) inset;} - 50% {box-shadow: 0 0 1rem var(--color-teresa--base) inset;} - 100% {box-shadow: 0 0 0.3rem var(--color-teresa--base) inset;} + 0% { box-shadow: 0 0 0.3rem var(--color-teresa--base) inset; } + 50% { box-shadow: 0 0 1rem var(--color-teresa--base) inset; } + 100% { box-shadow: 0 0 0.3rem var(--color-teresa--base) inset; } } -.s-base--metro .c-teresa-run-button, -.s-base--metro .c-teresa-unlock, -.s-base--metro .o-teresa-shop-button { - border-radius: 0; +.s-base--metro .c-teresa-unlock { border-width: 0.1rem; } .c-effarig-relics { + font-size: 1.5rem; padding: 20px; - font-size: 1.5em; } -/*#endregion Teresa tab*/ +/* #endregion Teresa tab */ /* #region effarig tab */ .c-effarig-shop-button { display: flex; - align-items: center; - justify-content: center; flex-direction: column; - line-height: 1.5; width: 35rem; - padding: 1rem; - font-size: 2rem; - background: var(--color-disabled); + justify-content: center; + align-items: center; + font-family: Typewriter; + font-size: 1.4rem; + line-height: 1.5; color: black; + background: var(--color-disabled); border: none; - border-radius: 0.5rem; + border-radius: var(--var-border-radius, 0.5rem); margin: 1rem; + padding: 1rem; transition-duration: 0.2s; } .c-effarig-shop-button--bought { - background-color: var(--color-effarig--base); color: black; + background-color: var(--color-effarig--base); + border: var(--var-border-width, 0.2rem) solid black; + box-shadow: 0.1rem 0.1rem 0.3rem rgba(0, 0, 0, 70%); cursor: default; - border: 0.2rem solid black; - box-shadow: 0.1rem 0.1rem 0.3rem rgba(0, 0, 0, 0.7); } .c-effarig-shop-button--available { @@ -6302,62 +6344,72 @@ kbd { } .c-effarig-shop-button--available:hover { - background-color: black; color: var(--color-effarig--base); + background-color: black; } .l-effarig-run-button { display: flex; - align-items: center; - justify-content: center; flex-direction: column; width: 15rem; height: 15rem; - border-radius: 50%; - border-style: none; position: relative; + justify-content: center; + align-items: center; + border-style: none; + border-radius: 50%; margin: 2rem; } .c-effarig-run-button { font-size: 10rem; font-weight: bold; - user-select: none; - cursor: pointer; transition-duration: 0.2s; + -webkit-user-select: none; + user-select: none; } - .c-effarig-run-button--not-running { - background-color: var(--color-effarig--base); - color: white; z-index: 0; - -webkit-animation: a-effarig-run-button--not-running-glow 2s ease-in-out infinite alternate; - -moz-animation: a-effarig-run-button--not-running-glow 2s ease-in-out infinite alternate; + color: white; + background-color: var(--color-effarig--base); animation: a-effarig-run-button--not-running-glow 2s ease-in-out infinite alternate; } .c-effarig-run-button--not-running:hover { - background-color: black; color: var(--color-effarig--base); + background-color: black; } @keyframes a-effarig-run-button--not-running-glow { from { - text-shadow: 0 0 1rem black, 0 0 2rem black, 0 0 3rem #cb1a1a, 0 0 4rem #cb1a1a, 0 0 5rem #cb1a1a, 0 0 6rem #cb1a1a, 0 0 7rem #cb1a1a; + text-shadow: + 0 0 1rem black, + 0 0 2rem black, + 0 0 3rem #cb1a1a, + 0 0 4rem #cb1a1a, + 0 0 5rem #cb1a1a, + 0 0 6rem #cb1a1a, + 0 0 7rem #cb1a1a; box-shadow: 0 0 0.5rem #cb1a1a, 0 0 1rem #cb1a1a, 0 0 1.5rem #cb1a1a; } + to { - text-shadow: 0 0 2rem black, 0 0 3rem #bf0404, 0 0 4rem #bf0404, 0 0 5rem #bf0404, 0 0 6rem #bf0404, 0 0 7rem #bf0404, 0 0 8rem #bf0404; + text-shadow: + 0 0 2rem black, + 0 0 3rem #bf0404, + 0 0 4rem #bf0404, + 0 0 5rem #bf0404, + 0 0 6rem #bf0404, + 0 0 7rem #bf0404, + 0 0 8rem #bf0404; box-shadow: 0 0 0.5rem #bf0404, 0 0 1rem #bf0404, 0 0 1.5rem #bf0404, 0 0 2rem #bf0404; } } .c-effarig-run-button--running { - background: black; z-index: 0; - -webkit-animation: a-effarig-run-button--running-glow 2s infinite alternate; - -moz-animation: a-effarig-run-button--running-glow 2s infinite alternate; + background: black; animation: a-effarig-run-button--running-glow 2s infinite alternate; } @@ -6371,88 +6423,128 @@ kbd { } .c-effarig-run-button__inner--running { - background-image: url("../images/noise.png"); + position: relative; -webkit-background-clip: text; background-clip: text; - -webkit-text-fill-color: rgba(255, 0, 0, 0.5); - -webkit-animation: a-effarig-run-button--running-noise 15s infinite alternate; - -moz-animation: a-effarig-run-button--running-noise 15s infinite alternate; + background-image: url("../images/noise.png"); animation: a-effarig-run-button--running-noise 15s infinite alternate; - position: relative; + + -webkit-text-fill-color: rgba(255, 0, 0, 50%); } - -.c-effarig-run-button__inner--running:after { - background: none; +.c-effarig-run-button__inner--running::after { content: attr(button-symbol); - z-index: -1; position: absolute; - -webkit-animation: a-effarig-run-button__inner--running-glow 2s infinite alternate; - -moz-animation: a-effarig-run-button__inner--running-glow 2s infinite alternate; - animation: a-effarig-run-button__inner--running-glow 2s infinite alternate; - left: 0; top: 0; + left: 0; + z-index: -1; + background: none; + animation: a-effarig-run-button__inner--running-glow 2s infinite alternate; } @keyframes a-effarig-run-button--running-noise { - 0% { -webkit-text-fill-color: rgba(255, 0, 0, 1); } - 20% { - -webkit-text-fill-color: rgba(255, 0, 0, 1); + 0% { + -webkit-text-fill-color: rgba(255, 0, 0, 100%); + } + + 20% { background-position: -50px 0; + + -webkit-text-fill-color: rgba(255, 0, 0, 100%); } - 22% { - -webkit-text-fill-color: rgba(255, 0, 0, 0.5); + + 22% { background-position: 100px -10px; + + -webkit-text-fill-color: rgba(255, 0, 0, 50%); } - 24% { - -webkit-text-fill-color: rgba(255, 0, 0, 1); + + 24% { background-position: 0 -150px; + + -webkit-text-fill-color: rgba(255, 0, 0, 100%); } - 50% { - -webkit-text-fill-color: rgba(255, 0, 0, 1); + + 50% { background-position: -50px 0; + + -webkit-text-fill-color: rgba(255, 0, 0, 100%); } - 51% { - -webkit-text-fill-color: rgba(255, 0, 0, 0.5); + + 51% { background-position: 100px -10px; + + -webkit-text-fill-color: rgba(255, 0, 0, 50%); } - 52% { - -webkit-text-fill-color: rgba(255, 0, 0, 1); + + 52% { background-position: 0 -150px; + + -webkit-text-fill-color: rgba(255, 0, 0, 100%); } - 54% { - -webkit-text-fill-color: rgba(255, 0, 0, 1); + + 54% { background-position: 50px 0; + + -webkit-text-fill-color: rgba(255, 0, 0, 100%); } - 56% { - -webkit-text-fill-color: rgba(255, 0, 0, 0.5); + + 56% { background-position: -100px 10px; + + -webkit-text-fill-color: rgba(255, 0, 0, 50%); } - 58% { - -webkit-text-fill-color: rgba(255, 0, 0, 1); + + 58% { background-position: 0 50px; + + -webkit-text-fill-color: rgba(255, 0, 0, 100%); } - 90% { - -webkit-text-fill-color: rgba(255, 0, 0, 1); + + 90% { background-position: 50px 50px; + + -webkit-text-fill-color: rgba(255, 0, 0, 100%); } - 91% { - -webkit-text-fill-color: rgba(255, 0, 0, 0.5); + + 91% { background-position: 100px -110px; + + -webkit-text-fill-color: rgba(255, 0, 0, 50%); } - 92% { - -webkit-text-fill-color: rgba(255, 0, 0, 1); + + 92% { background-position: 0 70px; + + -webkit-text-fill-color: rgba(255, 0, 0, 100%); + } + + 100% { + -webkit-text-fill-color: rgba(255, 0, 0, 100%); } - 100% { -webkit-text-fill-color: rgba(255, 0, 0, 1); } } @keyframes a-effarig-run-button--running-glow { from { - box-shadow: 0 0 0.5rem #c20707 inset, 0 0 1rem #c20707 inset, 0 0 1.5rem #c20707 inset, 0 0 1.5rem #c20707, 0 0 1.5rem #c20707, 0 0 1.5rem #c20707; + box-shadow: + 0 0 0.5rem #c20707 inset, + 0 0 1rem #c20707 inset, + 0 0 1.5rem #c20707 inset, + 0 0 1.5rem #c20707, + 0 0 1.5rem #c20707, + 0 0 1.5rem #c20707; } + to { - box-shadow: 0 0 0.5rem #e21717 inset, 0 0 1rem #e21717 inset, 0 0 1.5rem #e21717 inset, 0 0 1.5rem #e21717, 0 0 1.5rem #e21717, 0 0 1.5rem #e21717, 0 0 2rem #e21717 inset, 0 0 2rem #e21717; + box-shadow: + 0 0 0.5rem #e21717 inset, + 0 0 1rem #e21717 inset, + 0 0 1.5rem #e21717 inset, + 0 0 1.5rem #e21717, + 0 0 1.5rem #e21717, + 0 0 1.5rem #e21717, + 0 0 2rem #e21717 inset, + 0 0 2rem #e21717; } } @@ -6460,20 +6552,21 @@ kbd { from { text-shadow: 0 0 0.5rem #a20707, 0 0 1rem #a20707, 0 0 1.5rem #a20707; } + to { text-shadow: 0 0 0.5rem #c21717, 0 0 1rem #c21717, 0 0 1.5rem #c21717, 0 0 2rem #c21717; } } .l-effarig-run-button__bg { - position: absolute; width: 80%; height: 80%; + position: absolute; } .l-effarig-run-button__fg { position: relative; - background-color: rgba(0, 0, 0, 0); + background-color: rgba(0, 0, 0, 0%); } .l-effarig-shop { @@ -6490,27 +6583,33 @@ kbd { .l-effarig-run { display: inline-flex; flex-direction: column; - align-items: center; - margin-left: 3rem; width: 520px; + align-items: center; + border: var(--var-border-width, 0.2rem) solid var(--color-effarig--base); + border-radius: var(--var-border-radius, 0.5rem); + margin-left: 3rem; + padding: 1rem; } .c-effarig-run-description { font-size: larger; - margin-top: 1rem; + margin: 0.5rem 0; } .l-effarig-tab__reward { display: flex; flex-direction: row; - align-items: left; width: 100%; + align-items: left; margin: 0.5rem; } .c-effarig-tab__reward-label { - padding-right: 1rem; + /* stylelint-disable-next-line unit-allowed-list */ + min-width: 11ch; + text-align: left; font-weight: bold; + padding-right: 1rem; } .c-effarig-tab__reward-symbol { @@ -6533,11 +6632,10 @@ kbd { } .s-base--metro .c-effarig-shop-button { - border-radius: 0; border-width: 0.1rem; } -/* #endregion effarig tab*/ +/* #endregion effarig tab */ /* #startregion enslaved tab */ @@ -6572,36 +6670,35 @@ kbd { .l-enslaved-top-container__half { display: flex; flex-direction: column; - align-items: stretch; width: 24rem; + align-items: stretch; margin: 1rem; } -.o-enslaved-stored-time, .o-enslaved-gained-infinities { - font-size: 1.6rem; +.o-enslaved-stored-time, +.o-enslaved-gained-infinities { min-width: 18rem; + font-size: 1.6rem; padding-bottom: 0.5rem; } .o-enslaved-mechanic-button { - padding: 0.5rem; - border-radius: 0.5rem; - border: 0.2rem solid sandybrown; - box-shadow: 0 0 2rem inset rgba(244, 164, 96, 0.5); - background-color: #fdd3b0; - cursor: pointer; - margin: 1rem 0.5rem; min-width: 18rem; min-height: 7rem; - padding-bottom: 1rem; - padding-top: 1rem; font-family: Typewriter; + background-color: #fdd3b0; + border: var(--var-border-width, 0.2rem) solid sandybrown; + border-radius: var(--var-border-radius, 0.5rem); + box-shadow: 0 0 2rem inset rgba(244, 164, 96, 50%); + margin: 1rem 0.5rem; + padding: 0.5rem; + padding-top: 1rem; + padding-bottom: 1rem; transition-duration: 0.2s; } -.s-base--metro .o-enslaved-mechanic-button { - border-radius: 0; - border-width: 0.1rem; +.o-enslaved-mechanic-button--clickable { + cursor: pointer; } .o-enslaved-mechanic-button:hover { @@ -6616,94 +6713,91 @@ kbd { .l-enslaved-shop-container { display: flex; flex-wrap: wrap; - justify-content: center; width: 65rem; + justify-content: center; margin: auto; margin-top: 2rem; margin-bottom: 2rem; } .o-enslaved-shop-button { - padding: 1.7rem; - background-color: var(--color-celestials); - color: white; - border: none; - border-radius: 0.5rem; - margin: 1rem; - transition-duration: 0.2s; width: 30rem; height: 12rem; font-family: Typewriter; -} - -.s-base--metro .o-enslaved-shop-button { - border-radius: 0; + color: var(--color-text-inverted); + background-color: var(--color-disabled); + border: var(--var-border-width, 0.2rem) solid var(--color-bad); + border-radius: var(--var-border-radius, 0.5rem); + margin: 1rem; + padding: 1.7rem; + transition-duration: 0.2s; } .o-enslaved-shop-button--available { - background: aquamarine; - box-shadow: 0 0 1rem aquamarine; + color: var(--color-text); + background: none; + border: 0.2rem solid #ffa337; cursor: pointer; } .o-enslaved-shop-button--available:hover { - background: #16c55e; - box-shadow: 0 0 1rem #16c55e; + color: black; + background-color: #ffa337; + border: 0.2rem solid #ffa337; + transition: 0.2s; } .o-enslaved-shop-button--bought { - background: #16c55e; + color: black; + background-color: #ffa337; + border: none; pointer-events: none; } .c-enslaved-run-button { - width: 40rem; - padding: 2rem; - border-radius: 0.5rem; - border: 2px solid #cd945c; - background: none; - font-family: Typewriter; - font-size: 1.2rem; - margin-bottom: 1rem; - margin-top: 1rem; display: flex; flex-direction: column; + width: 34rem; align-items: center; -} - -.s-base--metro .c-enslaved-run-button { - border-radius: 0; - border-width: 0.1rem; + font-family: Typewriter; + font-size: 1.2rem; + background: none; + border: var(--var-border-width, 0.2rem) solid #cd945c; + border-radius: var(--var-border-radius, 0.5rem); + margin-top: 1rem; + margin-bottom: 3rem; + padding: 2rem 1rem; + -webkit-user-select: none; + user-select: none; } .c-enslaved-run-button__title { - font-weight: bold; font-size: larger; + font-weight: bold; margin-bottom: 0.5rem; } .c-enslaved-run-button__icon { - border-radius: 50%; - background: black; + display: flex; + overflow: hidden; + flex-direction: column; width: 16rem; height: 16rem; - margin: 1.5rem; - color: var(--color-enslaved-base); - cursor: pointer; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; position: relative; - overflow: hidden; - border: 0.4rem solid var(--color-enslaved-base); + justify-content: center; + align-items: center; + color: var(--color-enslaved--base); + background: black; + border: var(--var-border-width, 0.4rem) solid var(--color-enslaved--base); + border-radius: 50%; + margin: 1.5rem; transition-duration: 0.2s; animation: a-enslaved-run-button--spin 120s infinite linear; } .c-enslaved-run-button__icon:hover { color: black; - background: var(--color-enslaved-base); + background: var(--color-enslaved--base); border-color: black; } @@ -6717,38 +6811,51 @@ kbd { } .c-enslaved-run-button__icon__glitch { - position: absolute; - background-image: linear-gradient(rgba(0, 0, 0, 0) 0%, var(--color-enslaved-base) 20%, var(--color-enslaved-base) 80%, rgba(0,0,0,0) 100%); - border: none; width: 0.1rem; + position: absolute; + background-image: + linear-gradient( + rgba(0, 0, 0, 0%) 0%, + var(--color-enslaved--base) 20%, + var(--color-enslaved--base) 80%, + rgba(0, 0, 0, 0%) 100% + ); + border: none; } .c-enslaved-run-button__icon:hover .c-enslaved-run-button__icon__glitch { - background-image: linear-gradient(rgba(0, 0, 0, 0) 0%, black 20%, black 80%, rgba(0,0,0,0) 100%); -} - -.o-enslaved-release-header-button { - font-size: 1rem; - width: 20rem; + background-image: linear-gradient(rgba(0, 0, 0, 0%) 0%, black 20%, black 80%, rgba(0, 0, 0, 0%) 100%); } @keyframes a-enslaved-run-button--spin { - 0% {transform: rotate(0deg)} - 100% {transform: rotate(360deg)} + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } } .c-enslaved-hint-modal { display: flex; - flex-direction: column; overflow-y: scroll; - padding: 0.25rem 1rem; + flex-direction: column; width: 60rem; - height: 60rem; - font-size: 1.2rem; + height: 57rem; text-align: left; + font-size: 1.2rem; + padding: 0.25rem 1rem; } -/* #endregion enslaved tab*/ +.c-enslaved-hint-modal::-webkit-scrollbar { + width: 1rem; +} + +.c-enslaved-hint-modal::-webkit-scrollbar-thumb { + border: none; +} + +.s-base--metro .c-enslaved-hint-modal::-webkit-scrollbar-thumb { + border-radius: 0; +} + +/* #endregion enslaved tab */ /* #startregion v tab */ @@ -6760,24 +6867,24 @@ kbd { .l-v-unlocks-container { display: flex; + overflow: hidden; flex-wrap: wrap; - justify-content: center; width: 97.5rem; + justify-content: center; + box-sizing: border-box; margin: auto; margin-top: 1rem; margin-bottom: 1rem; - box-sizing: border-box; - overflow: hidden; } .l-v-unlocks-container li { - position: relative; - width: 27rem; - padding: 0 0 31.2rem; - list-style-type: none; - transform: rotate(-60deg) skewY(30deg); - overflow: hidden; visibility: visible; + overflow: hidden; + list-style-type: none; + width: 27rem; + position: relative; + padding: 0 0 31.2rem; + transform: rotate(-60deg) skewY(30deg); } /* required for hex structure */ @@ -6785,12 +6892,16 @@ kbd { margin: 0 1%; } -.l-v-unlocks-container li:nth-child(6n+4), .l-v-unlocks-container li:nth-child(6n+5), .l-v-unlocks-container li:nth-child(6n+6) { +.l-v-unlocks-container li:nth-child(6n+4), +.l-v-unlocks-container li:nth-child(6n+5), +.l-v-unlocks-container li:nth-child(6n+6) { margin-top: -7%; margin-bottom: -7%; } -.l-v-unlocks-container li:nth-child(6n+1), .l-v-unlocks-container li:nth-child(6n+2), .l-v-unlocks-container li:nth-child(6n+3) { +.l-v-unlocks-container li:nth-child(6n+1), +.l-v-unlocks-container li:nth-child(6n+2), +.l-v-unlocks-container li:nth-child(6n+3) { transform: translateX(50%) rotate(-60deg) skewY(30deg); } @@ -6804,8 +6915,8 @@ kbd { } .o-v-unlock-name { - font-weight: bold; font-size: 1.8rem; + font-weight: bold; } .o-v-unlock-desc { @@ -6813,8 +6924,8 @@ kbd { } .o-v-unlock-amount { - font-weight: bold; font-size: 1.3rem; + font-weight: bold; } .l-v-milestones-grid { @@ -6832,40 +6943,37 @@ kbd { .c-v-unlock-bar { width: 40rem; - padding: 0.6rem; - margin: 1rem 0 0; - border-radius: 0.5rem; - border: 0.2rem solid; position: relative; z-index: 0; + border: var(--var-border-width, 0.2rem) solid; + border-radius: var(--var-border-radius, 0.5rem); + margin-bottom: 1rem; + padding: 0.6rem; } .c-v-unlock-bar__progress { + height: 100%; position: absolute; top: 0; left: 0; - height: 100%; z-index: -1; } .o-v-milestone { - color: black; - background: #6b5f2e; - width: 25rem; - height: 10rem; - padding: 1rem; - border: 0.2rem solid #473f1f; - margin-right: 1rem; - border-radius: 0.5rem; - font-size: 1.2rem; display: flex; flex-direction: column; + width: 25rem; + height: 10rem; justify-content: space-around; -} - -.s-base--metro .o-v-milestone { - border-width: 0.1rem; - border-radius: 0; + font-size: 1.2rem; + color: black; + background: #6b5f2e; + border: var(--var-border-width, 0.2rem) solid #473f1f; + border-radius: var(--var-border-radius, 0.5rem); + margin: 0 0.5rem; + padding: 1rem; + -webkit-user-select: none; + user-select: none; } .o-v-milestone--unlocked { @@ -6878,36 +6986,38 @@ kbd { } .l-v-hexagon { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; position: absolute; top: 0; left: 0; - height: 100%; - width: 100%; - transform: skewY(-30deg) rotate(60deg); - display: flex; justify-content: center; align-items: center; - flex-direction: column; + transform: skewY(-30deg) rotate(60deg); + -webkit-user-select: none; + user-select: none; } .l-v-reduction { width: 3rem; height: 3rem; - border-radius: 50%; font-size: 1.5rem; + border-radius: var(--var-border-radius, 50%); + padding: 0; } .c-v-info-text { - color: var(--color-text); font-size: 1.2rem; + color: var(--color-text); } .c-v-run-button { - color: black; - background: var(--color-v--base); font-family: Typewriter; font-size: 1.3rem; - cursor: pointer; + color: black; + background: var(--color-v--base); padding: 0.5rem; transition-duration: 0.2s; } @@ -6921,8 +7031,8 @@ kbd { width: 12rem; height: 2.4rem; position: absolute; - background: #e3c759; z-index: -1; + background: #e3c759; transition-duration: 0.2s; } @@ -6950,42 +7060,90 @@ kbd { } .c-v-run-button--running .c-v-run-button__line--1 { - animation: a-c-v-run-button__line--1--move 3s infinite cubic-bezier(0.9, 0, 0, 0.9), a-c-v-run-button__line--burst 3s infinite cubic-bezier(0.9, 0, 0, 0.9); + animation: + a-c-v-run-button__line--1--move 3s infinite cubic-bezier(0.9, 0, 0, 0.9), + a-c-v-run-button__line--burst 3s infinite cubic-bezier(0.9, 0, 0, 0.9); } .c-v-run-button--running .c-v-run-button__line--2 { - animation: a-c-v-run-button__line--2--move 3s infinite cubic-bezier(0.9, 0, 0, 0.9), a-c-v-run-button__line--burst 3s infinite cubic-bezier(0.9, 0, 0, 0.9); + animation: + a-c-v-run-button__line--2--move 3s infinite cubic-bezier(0.9, 0, 0, 0.9), + a-c-v-run-button__line--burst 3s infinite cubic-bezier(0.9, 0, 0, 0.9); } .c-v-run-button--running .c-v-run-button__line--3 { - animation: a-c-v-run-button__line--3--move 3s infinite cubic-bezier(0.9, 0, 0, 0.9), a-c-v-run-button__line--burst 3s infinite cubic-bezier(0.9, 0, 0, 0.9); + animation: + a-c-v-run-button__line--3--move 3s infinite cubic-bezier(0.9, 0, 0, 0.9), + a-c-v-run-button__line--burst 3s infinite cubic-bezier(0.9, 0, 0, 0.9); } @keyframes a-c-v-run-button__line--1--move { - 0% {top: 23.4rem; left: 12.6rem; transform: rotate(-30deg);} - 50% {top: 23.4rem; left: 1.8rem; transform: rotate(30deg);} - 100% {top: 23.4rem; left: 12.6rem; transform: rotate(-30deg);} + 0% { + top: 23.4rem; + left: 12.6rem; + transform: rotate(-30deg); + } + + 50% { + top: 23.4rem; + left: 1.8rem; + transform: rotate(30deg); + } + + 100% { + top: 23.4rem; + left: 12.6rem; + transform: rotate(-30deg); + } } @keyframes a-c-v-run-button__line--2--move { - 0% {top: 14.4rem; left: -3rem; transform: rotate(90deg);} - 50% {top: 5.4rem; left: 2.4rem; transform: rotate(150deg);} - 100% {top: 14.4rem; left: -3rem; transform: rotate(90deg);} + 0% { + top: 14.4rem; + left: -3rem; + transform: rotate(90deg); + } + + 50% { + top: 5.4rem; + left: 2.4rem; + transform: rotate(150deg); + } + + 100% { + top: 14.4rem; + left: -3rem; + transform: rotate(90deg); + } } @keyframes a-c-v-run-button__line--3--move { - 0% {top: 5.4rem; left: 12.6rem; transform: rotate(30deg);} - 50% {top: 14.4rem; left: 17.4rem; transform: rotate(90deg);} - 100% {top: 5.4rem; left: 12.6rem; transform: rotate(30deg);} + 0% { + top: 5.4rem; + left: 12.6rem; + transform: rotate(30deg); + } + + 50% { + top: 14.4rem; + left: 17.4rem; + transform: rotate(90deg); + } + + 100% { + top: 5.4rem; + left: 12.6rem; + transform: rotate(30deg); + } } @keyframes a-c-v-run-button__line--burst { - 0% {box-shadow: 0 0 3rem black;} - 50% {box-shadow: 0 0 0 black;} - 100% {box-shadow: 0 0 3rem black;} + 0% { box-shadow: 0 0 3rem black; } + 50% { box-shadow: 0 0 0 black; } + 100% { box-shadow: 0 0 3rem black; } } -/* #endregion v tab*/ +/* #endregion v tab */ /* #startregion ra tab */ @@ -6997,14 +7155,13 @@ kbd { .c-ra-pet-header { width: 35rem; - padding: 1rem; - margin: 2rem auto; background: linear-gradient(#2f2f2f, #464646); - border-radius: 1.5rem; + border-radius: var(--var-border-radius, 1.5rem); + margin: 2rem auto; + padding: 1rem; } .s-base--metro .c-ra-pet-header { - border-radius: 0; border: 0.1rem solid black; } @@ -7014,22 +7171,23 @@ kbd { .l-ra-bar-container { display: flex; - margin: 0 0 2rem; width: 100%; height: 4rem; + margin: 0 0 2rem; margin-top: 1rem; margin-left: -0.2rem; } .l-ra-lvl-chevron { + height: 1.75rem; position: absolute; top: 0; - font-size: 1.2rem; z-index: 2; + font-size: 1.2rem; color: white; - height: 1.75rem; border-left: 0.1rem solid white; padding: 0 0.2rem; + -webkit-user-select: none; user-select: none; cursor: default; } @@ -7039,49 +7197,48 @@ kbd { } .c-important-chevron { - font-weight: bold; - font-size: 1.5rem; height: 4.6rem; - border-left-width: 0.2rem; + z-index: 3; + font-size: 1.5rem; + font-weight: bold; text-shadow: 0.1rem 0.1rem 0.1rem black, -0.1rem 0.1rem 0.1rem black, 0.1rem -0.1rem 0.1rem black, -0.1rem -0.1rem 0.1rem black, 0.1rem -0.1rem 0 black; - z-index: 3; + border-left-width: var(--var-border-width, 0.2rem); } .o-ra-unlock-hover-text { - background-color: black; - color: #fff; - text-align: center; - border-radius: 5px; - position: absolute; - z-index: 3; - font-size: 1rem; - left: -6.25rem; - bottom: 6rem; + display: flex; width: 12.5rem; height: fit-content; - display: flex; - padding: 1rem; + position: absolute; + bottom: 6rem; + left: -6.25rem; + z-index: 3; justify-content: center; + text-align: center; + font-size: 1rem; + color: #ffffff; + background-color: black; + border-radius: var(--var-border-radius, 5px); + padding: 1rem; } .c-ra-exp-bar { - position: relative; - border: 0.2rem solid black; - border-top-right-radius: 0; - border-bottom-right-radius: 0; width: 100%; height: 5rem; + position: relative; z-index: 1; + border: var(--var-border-width, 0.2rem) solid black; + border-top-right-radius: 0; + border-bottom-right-radius: 0; } .s-base--metro .c-ra-exp-bar { height: 5.1rem; - border-width: 0.1rem; margin-left: 0.1rem; } @@ -7091,14 +7248,13 @@ kbd { .c-ra-upgrade-bar { display: flex; - justify-content: flex-end; - border: 0.2rem solid black; - height: 2.6rem; width: 5rem; + height: 2.6rem; + justify-content: flex-end; + border: var(--var-border-width, 0.2rem) solid black; } .s-base--metro .c-ra-upgrade-bar { - border-width: 0.1rem; height: 2.625rem; } @@ -7109,7 +7265,6 @@ kbd { .l-ra-pet-container { float: left; width: 50%; - height: 50%; } .c-ra-memory-header { @@ -7122,27 +7277,27 @@ kbd { .c-ra-upgrade-icon { display: flex; - align-items: center; - justify-content: center; - font-size: 2.3rem; - box-shadow: 0.1rem 0.1rem 0.1rem rgba(0, 0, 0, 0.7); width: 4rem; height: 4rem; - background: #222; - border-radius: 50%; - border: 0.1rem solid #111; - margin: 0 0.3rem; - user-select: none; position: relative; + justify-content: center; + align-items: center; + font-size: 2.3rem; + background: #222222; + border: 0.1rem solid #111111; + border-radius: var(--var-border-radius, 50%); + box-shadow: 0.1rem 0.1rem 0.1rem rgba(0, 0, 0, 70%); + margin: 0 0.3rem; + -webkit-user-select: none; + user-select: none; } .s-base--metro .c-ra-upgrade-icon { - border-radius: 0; - border: 0.1rem solid black; + border-color: black; } .c-ra-upgrade-icon--inactive { - color: #555; + color: #555555; } .l-ra-non-pets { @@ -7153,39 +7308,33 @@ kbd { .c-ra-run-button { display: flex; flex-direction: column; - align-items: center; - justify-content: center; + width: 31rem; height: 33rem; + justify-content: center; + align-items: center; + font-family: Typewriter; color: #9575cd; background: black; - border: 0.2rem solid #9575cd; - border-radius: 2rem; - width: 30rem; + border: var(--var-border-width, 0.2rem) solid #9575cd; + border-radius: var(--var-border-radius, 2rem); margin: 2rem; padding: 1rem; - font-family: Typewriter; -} - -.s-base--metro .c-ra-run-button { - border-radius: 0; - border-width: 0.1rem; } .c-ra-run-button__icon { display: flex; - align-items: center; - justify-content: center; - color: #9575cd; - font-size: 3rem; - text-shadow: 0 0 1rem; - cursor: pointer; - height: 12rem; width: 12rem; - border-radius: 50%; + height: 12rem; + justify-content: center; + align-items: center; + font-size: 3rem; + color: #9575cd; + text-shadow: 0 0 1rem; background-color: black; - margin: 1.5rem 0; - border: 0.4rem solid #9575cd; + border: var(--var-border-width, 0.4rem) solid #9575cd; + border-radius: 50%; box-shadow: 0 0 0.7rem #9575cd, 0 0 0.7rem #9575cd inset; + margin: 1.5rem 0; transition-duration: 0.2s; } @@ -7205,141 +7354,141 @@ kbd { } .c-ra-run-button__icon--running .c-ra-run-button__icon__sigil { - animation: a-c-ra-run-button__icon__sigil--undulate 0.6s infinite ease-in-out, a-c-ra-run-button__icon__sigil--glow 3s infinite ease-in-out; + animation: + a-c-ra-run-button__icon__sigil--undulate 0.6s infinite ease-in-out, + a-c-ra-run-button__icon__sigil--glow 3s infinite ease-in-out; } @keyframes a-c-ra-run-button__icon__sigil--undulate { - 0% {transform: scale(0.9)} - 50% {transform: scale(1)} - 100% {transform: scale(0.9)} + 0% { transform: scale(0.9); } + 50% { transform: scale(1); } + 100% { transform: scale(0.9); } } @keyframes a-c-ra-run-button__icon__sigil--glow { - 0% {text-shadow: 0 0 1rem;} - 50% {text-shadow: 0 0 3rem;} - 100% {text-shadow: 0 0 1rem;} + 0% { text-shadow: 0 0 1rem; } + 50% { text-shadow: 0 0 3rem; } + 100% { text-shadow: 0 0 1rem; } } .c-ra-rewards { - font-size: 1.5rem; - margin: 1rem; display: flex; flex-direction: row; justify-content: center; + font-size: 1.5rem; + margin: 1rem; } + .c-ra-rewards-inner { margin: 0 1rem; } -.c-ra-recollection-unlock { +.c-ra-remembrance-unlock { display: flex; flex-direction: column; + width: 31rem; + height: 33rem; justify-content: center; align-items: center; - height: 25rem; color: white; background: linear-gradient(#333333, #464646); - border-radius: 2rem; - width: 30rem; + border-radius: var(--var-border-radius, 2rem); margin: 2rem; padding: 1rem; } -.s-base--metro .c-ra-recollection-unlock { - border-radius: 0; +.s-base--metro .c-ra-remembrance-unlock { border: 0.1rem solid black; } -.c-ra-recollection-unlock-inner { +.c-ra-remembrance-unlock-inner { display: flex; flex-wrap: wrap; justify-content: center; margin-top: 1rem; } -.c-ra-pet-recollection-button { - font-size: 1.2rem; +.c-ra-pet-remembrance-button { + width: 13rem; + height: 6.5rem; text-align: center; font-family: Typewriter; - width: 10rem; - height: 6rem; + font-size: 1.2rem; + border: 0.1rem solid #222222; + border-radius: var(--var-border-radius, 1rem); margin: 0.2rem; - border-radius: 1rem; - border: 0.1rem solid #222; - user-select: none; + cursor: pointer; } -.s-base--metro .c-ra-pet-recollection-button { - border-radius: 0; - border: 0.1rem solid black; +.s-base--metro .c-ra-pet-remembrance-button { + border-color: black; } .c-ra-laitela-unlock { display: flex; flex-direction: column; + width: 30rem; + height: 33rem; justify-content: center; align-items: center; font-family: Typewriter; color: black; - height: 33rem; - width: 30rem; + border-radius: var(--var-border-radius, 2rem); margin: 2rem; padding: 1rem; - border-radius: 2rem; animation: a-ra-color-shift 10s infinite; } .s-base--metro .c-ra-laitela-unlock { - border-radius: 0; - border: 0.1rem solid black; + border-color: black; } @keyframes a-ra-color-shift { - 0% { background-color: var(--color-ra-pet-teresa); } - 25% { background-color: var(--color-ra-pet-effarig); } - 50% { background-color: var(--color-ra-pet-enslaved); } - 75% { background-color: var(--color-ra-pet-v); } - 100% { background-color: var(--color-ra-pet-teresa); } + 0% { background-color: var(--color-ra-pet--teresa); } + 25% { background-color: var(--color-ra-pet--effarig); } + 50% { background-color: var(--color-ra-pet--enslaved); } + 75% { background-color: var(--color-ra-pet--v); } + 100% { background-color: var(--color-ra-pet--teresa); } } .l-ra-all-pets-container { display: flex; flex-wrap: wrap; + width: 80rem; + height: 100%; justify-content: center; align-items: center; margin: auto; - width: 80rem; - height: 100%; } .l-ra-unlock { - position: relative; + display: flex; + flex-direction: column; + width: 15%; height: 10rem; + position: relative; top: -10rem; left: 82.5%; - border: 0.2rem solid gainsboro; - background: #35d035; - width: 15%; - border-radius: 0.5rem; - flex-direction: column; - display: flex; justify-content: center; + background: #35d035; + border: var(--var-border-width, 0.2rem) solid gainsboro; + border-radius: var(--var-border-radius, 0.5rem); } .l-ra-unlock-inner { + display: flex; + flex: 1 0; + flex-direction: column; + justify-content: center; color: black; margin: auto 0.5rem; - display: flex; - flex-direction: column; - flex: 1 0; - justify-content: center; } .l-ra-alchemy-tab { display: flex; flex-direction: column; - align-items: center; position: relative; + align-items: center; } .l-ra-pet-upgrade-container { @@ -7353,23 +7502,21 @@ kbd { .c-ra-pet-upgrade { display: flex; + width: 2.5rem; + height: 2.6rem; + position: relative; justify-content: center; align-items: center; - height: 2.6rem; - width: 2.5rem; - background: grey; - color: black; - border: .2rem solid black; - border-right: 0; - border-radius: 1rem; font-size: 1.3rem; + color: black; + background: grey; + border: var(--var-border-width, 0.2rem) solid black; + border-right: 0; + border-radius: var(--var-border-radius, 1rem); transition-duration: 0.2s; - position: relative; } .s-base--metro .c-ra-pet-upgrade { - border-radius: 0; - border-width: 0.1rem; height: 2.625rem; } @@ -7389,100 +7536,96 @@ kbd { } .c-ra-pet-upgrade__tooltip { - opacity: 0; - transition-duration: 0.3s; - font-size: 1.4rem; - border: 0.1rem solid black; - border-radius: 0.8rem; - color: white; - background: #222; width: 24rem; - margin-left: 0; - padding: 0.4rem; - z-index: 2; - pointer-events: none; position: absolute; bottom: 100%; - box-shadow: 0.1rem 0.1rem 0.1rem rgba(0, 0, 0, 0.7); + z-index: 2; + font-size: 1.4rem; + opacity: 0; + color: white; + background: #222222; + border: 0.1rem solid black; + border-radius: var(--var-border-radius, 0.8rem); + box-shadow: 0.1rem 0.1rem 0.1rem rgba(0, 0, 0, 70%); + margin-left: 0; + padding: 0.4rem; + transition-duration: 0.3s; + pointer-events: none; } -.s-base--metro .c-ra-pet-upgrade__tooltip { - border-radius: 0; -} - -.c-ra-pet-upgrade__tooltip:after { +.c-ra-pet-upgrade__tooltip::after { + content: " "; + width: 0; position: absolute; bottom: 0; left: 50%; - margin-left: -0.7rem; - margin-bottom: 0; - width: 0; - border-top: 0 solid black; - border-right: 0.7rem solid transparent; - border-left: 0.7rem solid transparent; - content: " "; - transition-duration: 0.3s; z-index: 0; + border-top: 0 solid black; + border-right: var(--var-border-width, 0.7rem) solid transparent; + border-left: var(--var-border-width, 0.7rem) solid transparent; + margin-bottom: 0; + margin-left: -0.7rem; + transition-duration: 0.3s; } .c-ra-pet-upgrade:hover .c-ra-pet-upgrade__tooltip { - opacity: 1; bottom: calc(100% + 0.8rem); + opacity: 1; } .s-base--metro .c-ra-pet-upgrade:hover .c-ra-pet-upgrade__tooltip { - opacity: 1; bottom: calc(100% + 0.7rem); + opacity: 1; } -.c-ra-pet-upgrade:hover .c-ra-pet-upgrade__tooltip:after { +.c-ra-pet-upgrade:hover .c-ra-pet-upgrade__tooltip::after { border-top-width: 0.7rem; margin-bottom: -0.7rem; } .c-ra-level-up-btn:hover .c-ra-pet-upgrade__tooltip { - opacity: 1; bottom: calc(100% + 0.8rem); + opacity: 1; } .s-base--metro .c-ra-level-up-btn:hover .c-ra-pet-upgrade__tooltip { - opacity: 1; bottom: calc(100% + 0.7rem); + opacity: 1; } -.c-ra-level-up-btn:hover .c-ra-pet-upgrade__tooltip:after { +.c-ra-level-up-btn:hover .c-ra-pet-upgrade__tooltip::after { border-top-width: 0.7rem; margin-bottom: -0.7rem; } .c-ra-upgrade-icon:hover .c-ra-pet-upgrade__tooltip { - opacity: 1; bottom: calc(100% + 0.7rem); + opacity: 1; } -.c-ra-upgrade-icon:hover .c-ra-pet-upgrade__tooltip:after { +.c-ra-upgrade-icon:hover .c-ra-pet-upgrade__tooltip::after { border-top-width: 0.7rem; margin-bottom: -0.7rem; } .c-ra-upgrade-icon--teresa .c-ra-pet-upgrade__tooltip { - color: var(--color-ra-pet-teresa); + color: var(--color-ra-pet--teresa); } .c-ra-upgrade-icon--effarig .c-ra-pet-upgrade__tooltip { - color: var(--color-ra-pet-effarig); + color: var(--color-ra-pet--effarig); } -.c-ra-upgrade-icon--enslaved .c-ra-pet-upgrade__tooltip{ - color: var(--color-ra-pet-enslaved); +.c-ra-upgrade-icon--enslaved .c-ra-pet-upgrade__tooltip { + color: var(--color-ra-pet--enslaved); } .c-ra-upgrade-icon--v .c-ra-pet-upgrade__tooltip { - color: var(--color-ra-pet-v); + color: var(--color-ra-pet--v); } .c-ra-upgrade-icon--inactive .c-ra-pet-upgrade__tooltip { - color: #888; + color: #888888; } .c-ra-pet-upgrade__tooltip__name { @@ -7505,31 +7648,29 @@ kbd { .c-ra-level-up-btn { display: flex; + width: 3rem; + height: 5rem; + position: relative; justify-content: center; align-items: center; - height: 5rem; - width: 3rem; - background: grey; + font-size: 2rem; color: black; - border: 0.2rem solid black; + background: grey; + border: var(--var-border-width, 0.2rem) solid black; border-left: none; - border-radius: 1rem; + border-radius: var(--var-border-radius, 1rem); border-top-left-radius: 0; border-bottom-left-radius: 0; - font-size: 2rem; transition-duration: 0.2s; - position: relative; } .s-base--metro .c-ra-level-up-btn { height: 5.1rem; - border-radius: 0; - border-width: 0.1rem; } .c-ra-pet-btn--available { + background: #222222; cursor: pointer; - background: #222; } .c-ra-pet-btn--available:hover { @@ -7537,63 +7678,70 @@ kbd { } .c-ra-pet-btn--teresa { - color: var(--color-ra-pet-teresa); + color: var(--color-ra-pet--teresa); } -.c-ra-pet-btn--teresa:hover, .c-ra-pet-btn--teresa__capped { - background: var(--color-ra-pet-teresa); +.c-ra-pet-btn--teresa:hover, +.c-ra-pet-btn--teresa__capped { + background: var(--color-ra-pet--teresa); } .c-ra-pet-btn--effarig { - color: var(--color-ra-pet-effarig); + color: var(--color-ra-pet--effarig); } -.c-ra-pet-btn--effarig:hover, .c-ra-pet-btn--effarig__capped { - background: var(--color-ra-pet-effarig); +.c-ra-pet-btn--effarig:hover, +.c-ra-pet-btn--effarig__capped { + background: var(--color-ra-pet--effarig); +} + +.c-ra-pet-milestones-effarig-link { + font-weight: normal; } .c-ra-pet-btn--enslaved { - color: var(--color-ra-pet-enslaved); + color: var(--color-ra-pet--enslaved); } -.c-ra-pet-btn--enslaved:hover, .c-ra-pet-btn--enslaved__capped { - background: var(--color-ra-pet-enslaved); +.c-ra-pet-btn--enslaved:hover, +.c-ra-pet-btn--enslaved__capped { + background: var(--color-ra-pet--enslaved); } .c-ra-pet-btn--v { - color: var(--color-ra-pet-v); + color: var(--color-ra-pet--v); } -.c-ra-pet-btn--v:hover, .c-ra-pet-btn--v__capped { - background: var(--color-ra-pet-v); +.c-ra-pet-btn--v:hover, +.c-ra-pet-btn--v__capped { + background: var(--color-ra-pet--v); } .c-ra-pet-btn--available__capped { - cursor: default; color: black; + cursor: default; } .l-ra-pet-middle-container { display: flex; - align-items: center; - justify-content: center; - margin: 1.5rem 0; height: 4rem; + justify-content: center; + align-items: center; + margin: 1.5rem 0; padding: 0 1rem; } - .c-alchemy-resource-info { display: flex; flex-direction: column; - justify-content: center; - text-align: center; - border: 0.1rem solid black; - padding: 0.25rem 1rem; width: 60rem; height: 10rem; + justify-content: center; + text-align: center; font-size: 1.2rem; + border: 0.1rem solid black; margin-top: 0.5rem; + padding: 0.25rem 1rem; } .c-alchemy-resource-info--locked { @@ -7609,25 +7757,23 @@ kbd { .c-reality-glyph-creation { display: flex; flex-direction: column; - padding: 0.25rem 1rem; width: 60rem; - font-size: 1.2rem; - text-align: left; + padding: 0.25rem 1rem; } .l-alchemy-orbit-canvas { - position: absolute; overflow: visible; width: 100%; height: 100%; + position: absolute; left: 0; } .l-alchemy-arrow-canvas { - position: absolute; overflow: visible; width: 100%; height: 100%; + position: absolute; left: 0; } @@ -7659,14 +7805,14 @@ kbd { } @keyframes a-rainbow-stroke { - 0% {stroke: red} - 14.2% {stroke: orange} - 28.4% {stroke: yellow} - 42.6% {stroke: green} - 56.8% {stroke: blue} - 71% {stroke: indigo} - 85.2% {stroke: violet} - 100% {stroke: red} + 0% { stroke: red; } + 14.2% { stroke: orange; } + 28.4% { stroke: yellow; } + 42.6% { stroke: green; } + 56.8% { stroke: blue; } + 71% { stroke: indigo; } + 85.2% { stroke: violet; } + 100% { stroke: red; } } .o-alchemy-reaction-arrow--focused { @@ -7689,46 +7835,59 @@ kbd { transition-duration: 0.2s; } -.o-alchemy-reaction-path--limited { +.o-alchemy-reaction-path--capped { stroke: #ff9800; stroke-width: 2; opacity: 1; transition-duration: 0.2s; } +.o-alchemy-reaction-path--less-than-required { + stroke: #ff0000; + stroke-width: 1; + opacity: 1; + transition-duration: 0.2s; +} + .o-alchemy-reaction-path--not-focused { - opacity: 0.5; + opacity: 0.35; +} + +.o-alchemy-reaction-path--doomed { + stroke: var(--color-pelle--base); + opacity: 0.6; } .o-alchemy-node { - color: black; - position: absolute; width: 3rem; height: 3rem; - margin-left: -1.5rem; - margin-top: -1.5rem; - user-select: none; + position: absolute; + z-index: 1; font-size: 2rem; + line-height: 1.9rem; + color: black; + background-color: white; border: 0.1rem solid black; border-radius: 50%; - background-color: white; + margin-top: -1.5rem; + margin-left: -1.5rem; transition: all 0.2s, z-index 0s; - z-index: 1; - line-height: 1.9rem; + -webkit-user-select: none; + user-select: none; } .o-alchemy-node-mask { - position: absolute; width: 2.4rem; height: 2.4rem; - left: 50%; + position: absolute; top: 50%; - margin-left: -1.2rem; - margin-top: -1.2rem; - background-color: #bbb; + left: 50%; + z-index: 4; + background-color: #bbbbbb; border: 0.1rem solid black; border-radius: 50%; - z-index: 4; + margin-top: -1.2rem; + margin-left: -1.2rem; } .o-alchemy-resource-arc-wrapper { @@ -7747,37 +7906,38 @@ kbd { } .o-alchemy-resource-arc-spinner { - border-radius: 1.4rem 0 0 1.4rem; z-index: 2; + border-radius: 1.4rem 0 0 1.4rem; transform-origin: 100% 50%; } .o-alchemy-resource-arc-filler { - border-radius: 0 1.4rem 1.4rem 0; - z-index: 1; left: 50%; + z-index: 1; opacity: 0; + border-radius: 0 1.4rem 1.4rem 0; } .o-alchemy-resource-arc-mask { width: 50%; height: 100%; - border-radius: 1.4rem 0 0 1.4rem; position: absolute; z-index: 3; opacity: 1; background: inherit; + border-radius: 1.4rem 0 0 1.4rem; } .o-alchemy-node--unfocused { - opacity: 0.5; z-index: 4; + opacity: 0.35; } .o-alchemy-node--locked { - opacity: 0.25; z-index: 4; + opacity: 0.25; } + .o-alchemy-node--base { background-color: #81d4fa; } @@ -7786,11 +7946,6 @@ kbd { background-color: #9ccc65; } -.o-alchemy-node--doomed { - background-color: crimson; -} - - .c-ra-unlock-unlocked { background: lightgreen; } @@ -7802,18 +7957,19 @@ kbd { } .o-ra-teresa-switch { - border: 0.2rem solid #292929; - width: 5rem; - margin-left: -0.2rem; - height: 2rem; display: flex; + width: 5rem; + height: 2rem; justify-content: center; align-items: center; - font-weight: bold; font-size: 1.1rem; - cursor: pointer; + font-weight: bold; color: black; + border: var(--var-border-width, 0.2rem) solid #292929; + margin-left: -0.2rem; + -webkit-user-select: none; user-select: none; + cursor: pointer; } .o-ra-teresa-switch-active { @@ -7829,12 +7985,12 @@ kbd { } .o-ra-teresa-switch-idle-on { - background: #00e6ff + background: #00e6ff; } -/* #endregion ra tab*/ +/* #endregion ra tab */ -/* #startregion laitela tab*/ +/* #startregion laitela tab */ .l-laitela-celestial-tab { display: flex; @@ -7844,20 +8000,15 @@ kbd { .o-laitela-run-button { width: 22rem; - padding: 2rem; - border-radius: 0.5rem; - color: var(--color-laitela--accent); - border: 0.2rem solid var(--color-laitela--accent); - background: var(--color-laitela--base); - font-family: Typewriter; - font-size: 1.2rem; height: 84rem; + font-family: Typewriter; + font-size: 1.1rem; + color: var(--color-laitela--accent); + background: var(--color-laitela--base); + border: var(--var-border-width, 0.2rem) solid var(--color-laitela--accent); + border-radius: var(--var-border-radius, 0.5rem); margin-right: 1rem; -} - -.s-base--metro .o-laitela-run-button { - border-width: 0.1rem; - border-radius: 0; + padding: 2rem; } .o-laitela-run-button--large { @@ -7872,20 +8023,15 @@ kbd { .c-dark-matter-dimension-container { display: flex; flex-direction: column; - color: var(--color-laitela--accent); - border: 0.2rem solid var(--color-laitela--accent); - background: var(--color-laitela--base); width: 50rem; - margin: auto; - padding: 0.5rem; - border-radius: 0.5rem; - margin-bottom: 1rem; height: 15rem; -} - -.s-base--metro .c-dark-matter-dimension-container { - border-width: 0.1rem; - border-radius: 0; + color: var(--color-laitela--accent); + background: var(--color-laitela--base); + border: var(--var-border-width, 0.2rem) solid var(--color-laitela--accent); + border-radius: var(--var-border-radius, 0.5rem); + margin: auto; + margin-bottom: 1rem; + padding: 0.5rem; } .o-dark-matter-dimension-amount { @@ -7895,36 +8041,29 @@ kbd { .c-dark-matter-dimension-buttons { display: flex; - flex-direction: row; - justify-content: space-between; - width: 47.5rem; height: 6rem; + justify-content: space-around; } .o-dark-matter-dimension-button { width: 15rem; height: 5rem; - margin-bottom: 1rem; font-family: Typewriter; color: var(--color-laitela--accent); background: var(--color-laitela--base); - border: 0.2rem solid var(--color-laitela--accent); - border-radius: 0.5rem; + border: var(--var-border-width, 0.2rem) solid var(--color-laitela--accent); + border-radius: var(--var-border-radius, 0.5rem); + margin-bottom: 1rem; transition-duration: 0.15s; } -.s-base--metro .o-dark-matter-dimension-button { - border-width: 0.1rem; - border-radius: 0; -} - .o-dark-matter-dimension-button--available { + font-weight: bold; color: var(--color-laitela--base); background: var(--color-laitela--accent); border-color: var(--color-laitela--accent); box-shadow: 0 0 0.5rem 0.1rem inset, 0 0 0.3rem 0; cursor: pointer; - font-weight: bold; } .o-dark-matter-dimension-button--available:hover { @@ -7934,13 +8073,13 @@ kbd { } .o-dark-matter-dimension-button--ascend { + font-weight: bold; color: var(--color-laitela--base); background: var(--color-laitela--accent); border-color: var(--color-laitela--accent); box-shadow: 0 0 0.5rem 0.1rem inset, 0 0 0.3rem 0; - cursor: pointer; - font-weight: bold; animation: 3s a-laitela-flash infinite; + cursor: pointer; } .o-dark-matter-dimension-button--accent { @@ -7952,10 +8091,12 @@ kbd { color: var(--color-laitela--base); background: var(--color-laitela--accent); } + 50% { color: var(--color-laitela--accent); background: var(--color-laitela--base); } + 100% { color: var(--color-laitela--base); background: var(--color-laitela--accent); @@ -7973,10 +8114,12 @@ kbd { color: black; background: white; } + 50% { color: white; background: black; } + 100% { color: black; background: white; @@ -7985,53 +8128,49 @@ kbd { .l-laitela-mechanics-container { display: flex; - align-items: flex-start; justify-content: center; + align-items: flex-start; } .c-laitela-milestone { display: flex; flex-direction: column; - justify-content: center; - height: 16%; width: 22rem; - background: var(--color-laitela--base); - padding: 0.6rem; - margin: 1rem 0 0; - border-radius: 0.5rem; - border: 0.1rem solid; + height: 16%; position: relative; z-index: 0; + justify-content: center; color: var(--color-laitela--accent); + background: var(--color-laitela--base); + border: 0.1rem solid; + border-radius: var(--var-border-radius, 0.5rem); + margin: 1rem 0 0; + padding: 0.6rem; } .c-laitela-milestone > span { - height: 100%; display: flex; flex-direction: column; + height: 100%; justify-content: space-around; } -.s-base--metro .c-laitela-milestone { - border-radius: 0; -} - .c-laitela-milestone__progress { + height: 100%; position: absolute; top: 0; left: 0; - height: 100%; z-index: -1; } .c-laitela-milestone--bar-border-fix { - position: absolute; - height: calc(100% + 0.2rem); width: calc(100% + 0.2rem); - border-radius: 0.5rem; - border: 0.2rem solid; + height: calc(100% + 0.2rem); + position: absolute; z-index: 1; color: var(--color-laitela--accent); + border: var(--var-border-width, 0.2rem) solid; + border-radius: var(--var-border-radius, 0.5rem); margin: -0.7rem; } @@ -8040,64 +8179,54 @@ kbd { } .c-laitela-milestone-mask { + z-index: 0; background-color: white; mix-blend-mode: difference; - z-index: 0; } .c-laitela-milestone--completed { - filter: brightness(10%); - left: 1.5rem; - top: -1.5rem; - height: 21.8rem; width: 18.8rem; - transform: rotate(90deg); - background-image: url(../images/laitela-icon.svg); + height: 21.8rem; + top: -1.5rem; + left: 1.5rem; + background-color: transparent; + background-image: url("../images/laitela-icon.svg"); background-position: center; background-repeat: repeat-y; - background-color: transparent; + transform: rotate(90deg); + filter: brightness(10%); } .c-laitela-singularity-container { - color: var(--color-laitela--accent); - background: var(--color-laitela--base); - width: 96rem; - margin: auto; - border: 0.2rem solid var(--color-laitela--accent); - border-radius: 0.5rem; - margin-bottom: 1rem; display: flex; + width: 96rem; justify-content: space-around; align-items: center; - padding: 1rem; + color: var(--color-laitela--accent); + background: var(--color-laitela--base); + border: var(--var-border-width, 0.2rem) solid var(--color-laitela--accent); + border-radius: var(--var-border-radius, 0.5rem); + margin: auto; margin-top: 1rem; -} - -.s-base--metro .c-laitela-singularity-container { - border-width: 0.1rem; - border-radius: 0; + margin-bottom: 1rem; + padding: 1rem; } .c-laitela-singularity { - color: white; - background: black; - padding: 2rem; width: 40rem; height: 11rem; - border: 0.2rem solid white; - border-radius: 0.5rem; + color: white; + background: black; + border: var(--var-border-width, 0.2rem) solid white; + border-radius: var(--var-border-radius, 0.5rem); + padding: 2rem; transition-duration: 0.15s; } -.s-base--metro .c-laitela-singularity { - border-width: 0.1rem; - border-radius: 0; -} - .c-laitela-singularity h2 { - margin: 0; font-family: Typewriter; font-size: 1.5rem; + margin: 0; } .c-laitela-singularity--active { @@ -8111,23 +8240,21 @@ kbd { } .c-laitela-singularity__cap-control { - color: var(--color-laitela--accent); - background: var(--color-laitela--base); - padding: 1rem; width: 20rem; - margin-bottom: 1rem; - border: 0.2rem solid var(--color-laitela--accent); - border-radius: .5rem; - cursor: pointer; font-family: Typewriter; font-size: 1.1rem; font-weight: bold; + color: var(--color-laitela--accent); + background: var(--color-laitela--base); + border: var(--var-border-width, 0.2rem) solid var(--color-laitela--accent); + border-radius: var(--var-border-radius, 0.5rem); + margin-bottom: 1rem; + padding: 1rem; transition-duration: 0.15s; } -.s-base--metro .c-laitela-singularity__cap-control { - border-width: 0.1rem; - border-radius: 0; +.c-laitela-singularity__cap-control--available { + cursor: pointer; } .c-laitela-singularity__cap-control:hover { @@ -8140,9 +8267,9 @@ kbd { } .l-singularity-milestone-modal-container-outer { - height: 50rem; - overflow-y: scroll; overflow-x: hidden; + overflow-y: scroll; + height: 50rem; } .l-singularity-milestone-modal-container-outer::-webkit-scrollbar { @@ -8150,15 +8277,18 @@ kbd { } .l-singularity-milestone-modal-container-outer::-webkit-scrollbar-thumb { - background: grey; - border-radius: 0.5rem; + border: none; +} + +.s-base--metro .l-singularity-milestone-modal-container-outer::-webkit-scrollbar-thumb { + border-radius: 0; } .l-singularity-milestone-modal-container-inner { display: flex; flex-wrap: wrap; - height: 120rem; width: 93rem; + height: 120rem; justify-content: space-evenly; padding-bottom: 2rem; } @@ -8168,32 +8298,29 @@ kbd { } .l-singularity-milestone-sort-container { - position: relative; - left: -0.5rem; display: flex; flex-direction: row; - margin-top: 0.5rem; + position: relative; + left: -0.5rem; border: 0.1rem solid var(--color-laitela--accent); + border-radius: var(--var-border-radius, 0.6rem); + margin-top: 0.5rem; padding: 1rem 0.5rem; } .c-singularity-milestone-modal-sort-button { - height: 6rem; width: 22rem; + height: 6rem; font-family: Typewriter, serif; - font-weight: bold; font-size: 1.3rem; + font-weight: bold; color: var(--color-laitela--accent); background-color: var(--color-laitela--base); border: 0.1rem solid var(--color-laitela--accent); - border-radius: 0.5rem; - cursor: pointer; + border-radius: var(--var-border-radius, 0.5rem); margin: 0 0.5rem; transition-duration: 0.15s; -} - -.s-base--metro .c-singularity-milestone-modal-sort-button { - border-radius: 0; + cursor: pointer; } .c-singularity-milestone-modal-sort-button:hover { @@ -8202,22 +8329,17 @@ kbd { } .o-laitela-singularity-modal-button { - color: var(--color-laitela--accent); - background: var(--color-laitela--base); - padding: 0.6rem; - margin: 0; - font-weight: bold; width: 22rem; height: 4%; - border-radius: .5rem; - border: 0.2rem solid var(--color-laitela--accent); - cursor: pointer; + font-weight: bold; + color: var(--color-laitela--accent); + background: var(--color-laitela--base); + border: var(--var-border-width, 0.2rem) solid var(--color-laitela--accent); + border-radius: var(--var-border-radius, 0.5rem); + margin: 0; + padding: 0.6rem; transition-duration: 0.15s; -} - -.s-base--metro .o-laitela-singularity-modal-button { - border-width: 0.1rem; - border-radius: 0; + cursor: pointer; } .o-laitela-singularity-modal-button:hover { @@ -8226,28 +8348,24 @@ kbd { } .o-laitela-singularity-locked { - color: var(--color-laitela--accent); - background: var(--color-laitela--base); - padding: 0.6rem; - margin: 0; - font-weight: bold; width: 22rem; height: 4%; - border-radius: .5rem; - border: 0.2rem solid var(--color-laitela--accent); font-size: 20px; + font-weight: bold; + color: var(--color-laitela--accent); + background: var(--color-laitela--base); + border: var(--var-border-width, 0.2rem) solid var(--color-laitela--accent); + border-radius: var(--var-border-radius, 0.5rem); + margin: 0; + padding: 0.6rem; padding-top: 10rem; } -.s-base--metro .o-laitela-singularity-locked { - border-width: 0.1rem; - border-radius: 0; -} .c-laitela-next-milestones { - margin-left: 1rem; display: flex; flex-direction: column; height: 84rem; + margin-left: 1rem; } @keyframes a-dark-energy-glow { @@ -8257,25 +8375,24 @@ kbd { } .o-laitela-run-button__icon { - height: 12rem; width: 12rem; - border-radius: 50%; + height: 12rem; + align-items: center; background-color: black; - background-image: url(../images/laitela-icon.svg); + background-image: url("../images/laitela-icon.svg"); background-position: center; background-repeat: repeat-y; background-size: 4.95rem; - align-items: center; + border: var(--var-border-width, 0.4rem) solid var(--color-laitela--accent); + border-radius: 50%; margin: 1.5rem auto; - border: 0.4rem solid var(--color-laitela--accent); - animation: a-laitela-run-button__icon--scroll 15s infinite linear; transition-duration: 0.2s; - cursor: pointer; + animation: a-laitela-run-button__icon--scroll 15s infinite linear; } .o-laitela-run-button__icon:hover { - background-image: url(../images/laitela-icon-dark.svg); background-color: white; + background-image: url("../images/laitela-icon-dark.svg"); } .o-laitela-run-button__icon--running { @@ -8283,46 +8400,36 @@ kbd { } @keyframes a-laitela-run-button__icon--scroll { - 0% {background-position-y: 0} - 100% {background-position-y: 24rem} + 0% { background-position-y: 0; } + 100% { background-position-y: 24rem; } } .l-laitela-annihilation-container { - color: var(--color-laitela--accent); - background: var(--color-laitela--base); - padding: 1rem; width: 50rem; height: 20rem; - border: 0.2rem solid var(--color-laitela--accent); - border-radius: 0.5rem; font-family: Typewriter, serif; font-size: 1.2rem; -} - -.s-base--metro .l-laitela-annihilation-container { - border-width: 0.1rem; - border-radius: 0; + color: var(--color-laitela--accent); + background: var(--color-laitela--base); + border: var(--var-border-width, 0.2rem) solid var(--color-laitela--accent); + border-radius: var(--var-border-radius, 0.5rem); + padding: 1rem; } .l-laitela-annihilation-button { - color: var(--color-laitela--accent); - background: var(--color-laitela--base); - padding: 0.5rem; width: 35rem; height: 3rem; - border: 0.2rem solid var(--color-laitela--accent); - border-radius: 0.5rem; font-family: Typewriter, serif; + color: var(--color-laitela--accent); + background: var(--color-laitela--base); + border: var(--var-border-width, 0.2rem) solid var(--color-laitela--accent); + border-radius: var(--var-border-radius, 0.5rem); + padding: 0.5rem; } .c-laitela-annihilation-button { - cursor: pointer; transition-duration: 0.3s; -} - -.s-base--metro .l-laitela-annihilation-button { - border-width: 0.1rem; - border-radius: 0; + cursor: pointer; } .c-laitela-annihilation-button h2 { @@ -8334,8 +8441,8 @@ kbd { } .c-laitela-annihilation-button:hover { - background: var(--color-laitela--accent); color: var(--color-laitela--base) !important; /* h2 color overrides this */ + background: var(--color-laitela--accent); } .c-laitela-annihilation-input { @@ -8343,18 +8450,18 @@ kbd { } .c-laitela-automation-toggle { - color: var(--color-laitela--accent); - background: var(--color-laitela--base); - padding: 0.5rem; width: 20rem; height: 3rem; - border: 0.2rem solid var(--color-laitela--accent); - border-radius: .5rem; - cursor: pointer; font-family: Typewriter; font-size: 1.1rem; font-weight: bold; + color: var(--color-laitela--accent); + background: var(--color-laitela--base); + border: var(--var-border-width, 0.2rem) solid var(--color-laitela--accent); + border-radius: var(--var-border-radius, 0.5rem); + padding: 0.5rem; transition-duration: 0.15s; + cursor: pointer; } .c-laitela-automation-toggle:hover { @@ -8366,40 +8473,36 @@ kbd { box-shadow: none; } -/* #endregion laitela tab*/ - +/* #endregion laitela tab */ .c-performance-stats { width: 25rem; - padding: 0.3rem; position: fixed; top: 0; left: 0; - border: 0.1rem solid black; + z-index: 999; text-align: left; background-color: whitesmoke; - z-index: 999; + border: 0.1rem solid black; + padding: 0.3rem; } .c-glyph-level-factors-dropdown-header { - color: var(--color-reality-light); width: calc(100% - 1rem); - font-weight: bold; text-align: center; - background: black; - border: 0.2rem solid var(--color-reality); - border-radius: 0.5rem; + color: var(--color-reality-dark); + background: var(--color-background); + border: var(--var-border-width, 0.2rem) solid var(--color-reality-dark); + border-radius: var(--var-border-radius, 0.5rem); padding-top: 0.3rem; padding-bottom: 0.3rem; } -.s-base--metro .c-glyph-level-factors-dropdown-header { - border-width: 0.1rem; - border-radius: 0; +.s-base--dark .c-glyph-level-factors-dropdown-header { + color: var(--color-reality-light); } .l-glyph-levels-and-weights { - display: -ms-grid; display: grid; width: 100%; padding-top: 1rem; @@ -8407,116 +8510,115 @@ kbd { .c-glyph-levels-and-weights { font-weight: bold; - background: black; + background: var(--color-background); } .l-glyph-levels-and-weights__adjust-label { - grid-column: 4; - -ms-grid-column: 4; - -ms-grid-column-span: 2; - grid-row: 5; - -ms-grid-row: 5; white-space: nowrap; - font-weight: bold; - padding: 0.25em 0 .25em 1em; + grid-row: 5; + grid-column: 4; z-index: 2; + font-weight: bold; + padding: 0.25rem 0 0.25rem 1rem; } .l-glyph-levels-and-weights__adjust-auto { - grid-column: 4; - -ms-grid-column: 4; - -ms-grid-column-span: 2; - grid-row: 6; - -ms-grid-row: 6; white-space: nowrap; - font-weight: bold; - padding: 0.25em 0 .25em 1em; + grid-row: 6; + grid-column: 4; z-index: 2; + font-weight: bold; + padding: 0.25rem 0 0.25rem 1rem; } .l-glyph-levels-and-weights__adjust-outline { - grid-column: 4 / 5; - -ms-grid-column: 4; - -ms-grid-column-span: 1; - -ms-grid-row: 1; - position:relative; + grid-row: 1; + grid-column: 4; + position: relative; } .l-glyph-levels-and-weights__adjust-outline::after { - content: ''; + content: ""; position: absolute; - border: 0.2rem solid var(--color-reality-light); - border-radius: 0.5em; - top: -0.5em; - bottom: -0.5em; - left: -0.5em; - right: -0.5em; + top: -0.5rem; + right: -0.5rem; + bottom: -0.5rem; + left: -0.5rem; + border: var(--var-border-width, 0.2rem) solid var(--color-reality-dark); + border-radius: var(--var-border-radius, 0.5rem); } -.s-base--metro .l-glyph-levels-and-weights__adjust-outline::after { - border-width: 0.1rem; - border-radius: 0; +.s-base--dark .l-glyph-levels-and-weights__adjust-outline::after { + border-color: var(--color-reality-light); } - .l-glyph-levels-and-weights__factor { + display: flex; grid-column: 1; - -ms-grid-column: 1; - text-align: left; - padding-left: 1em; - padding-top: 0.3em; - padding-bottom: 0.3em; - display:flex; align-items: center; + text-align: left; + padding-top: 0.3rem; + padding-bottom: 0.3rem; + padding-left: 1rem; } .l-glyph-levels-and-weights__operator { + display: flex; grid-column: 2; - -ms-grid-column: 2; - display:flex; align-items: center; - padding-left: 1em; + padding-left: 1rem; } .l-glyph-levels-and-weights__factor-val { - grid-column: 3; - -ms-grid-column: 3; - text-align: right; - padding-left: 1em; display: flex; - align-items: center; + grid-column: 3; justify-content: flex-end; - padding-right: 2em; + align-items: center; + text-align: right; + padding-right: 2rem; + padding-left: 1rem; } .l-glyph-levels-and-weights__slider { - grid-column: 4; - -ms-grid-column: 4; display: flex; + grid-column: 4; align-items: center; margin-top: 0.4rem; margin-bottom: 0.4rem; } .c-glyph-levels-and-weights__slider-process { + background-color: var(--color-reality); +} + +.s-base--dark .c-glyph-levels-and-weights__slider-process { background-color: var(--color-reality-light); } .c-glyph-levels-and-weights__slider-bg { + background-color: var(--color-reality-dark); +} + +.s-base--dark .c-glyph-levels-and-weights__slider-bg { background-color: var(--color-reality); } .c-glyph-levels-and-weights__slider-handle { - color: var(--color-reality-light); - background-color: black; + color: var(--color-reality); + background-color: var(--color-background); + border-radius: var(--var-border-radius, 0.8rem); box-shadow: 0 0 0 0.1rem var(--color-reality-light); - border-radius: 0.8rem; transition-duration: 0.2s; } +.s-base--dark .c-glyph-levels-and-weights__slider-handle { + color: var(--color-reality-light); +} + .c-glyph-levels-and-weights__slider-handle:hover { color: black; background-color: var(--color-reality-light); + border-color: var(--color-reality-light); } .l-glyph-levels-and-weights__reset-btn-outer { @@ -8524,57 +8626,62 @@ kbd { } .c-glyph-levels-and-weights__reset-btn { - color: var(--color-reality-light); - background-color: black; - border-radius: 0.5rem; + color: var(--color-reality); + background-color: var(--color-background); border: 0.1rem solid var(--color-reality-light); + border-radius: var(--var-border-radius, 0.5rem); padding: 0.2rem 0.5rem; transition-duration: 0.2s; + user-select: none; +} + +.c-glyph-levels-and-weights__reset-btn-clickable { cursor: pointer; } +.s-base--dark .c-glyph-levels-and-weights__reset-btn { + color: var(--color-reality-light); +} + .c-glyph-levels-and-weights__reset-btn:hover { color: black; background-color: var(--color-reality-light); } -.s-base--metro .c-glyph-levels-and-weights__reset-btn { - border-radius: 0; -} - .c-glyph-levels-and-weights__auto-btn { - font-size: 1rem; - font-family: Typewriter; - font-weight: bold; - color: var(--color-reality-light); - background-color: black; height: 2.5rem; - border-radius: 0.5rem; + font-family: Typewriter; + font-size: 1rem; + font-weight: bold; + color: var(--color-reality); + background-color: var(--color-background); border: 0.1rem solid var(--color-reality-light); + border-radius: var(--var-border-radius, 0.5rem); padding: 0.2rem 0.5rem; transition-duration: 0.2s; cursor: pointer; } +.s-base--dark .c-glyph-levels-and-weights__auto-btn { + color: var(--color-reality-light); +} + .c-glyph-levels-and-weights__auto-btn:hover { color: black; background-color: var(--color-reality-light); } -.s-base--metro .c-glyph-levels-and-weights__auto-btn { - border-radius: 0; -} - .o-questionmark { - border-radius: 50%; - border: 0.1rem solid; - width: 1.2rem; - height: 1.2rem; - font-size: 1rem; display: inline-flex; - align-items: center; - justify-content: center; + width: 1.4rem; + height: 1.4rem; z-index: 2; + justify-content: center; + align-items: center; + font-size: 1rem; + border: 0.1rem solid; + border-radius: var(--var-border-radius, 50%); + -webkit-user-select: none; user-select: none; } @@ -8587,8 +8694,8 @@ kbd { .l-reality-button-column { display: flex; flex-direction: column; - align-items: center; width: 44rem; + align-items: center; margin-right: 2rem; } @@ -8596,33 +8703,24 @@ kbd { display: flex; flex-direction: row; width: 100%; - height: 9rem; - margin-top: 0.5rem; - margin-bottom: 1rem; -} - -.l-reality-button-group-half { - display: flex; - flex-direction: column; - justify-content: space-between; - width: 50%; + height: 6rem; padding: 0.5rem; } .l-player-glyphs-column { display: flex; flex-direction: column; - padding-top: 1rem; - justify-content: flex-start; width: 65rem; + justify-content: flex-start; + padding-top: 1rem; } .l-reality-upgrade-grid { display: flex; flex-direction: column; - margin-top: 1.2rem; width: 100%; align-items: center; + margin-top: 1.2rem; } .l-reality-upgrade-grid__row { @@ -8631,27 +8729,31 @@ kbd { } .l-reality-upgrade-btn { - height: 10rem; - padding: 0 0.5rem; display: flex; + flex-direction: column; + width: 20.5rem; + height: 10rem; + position: relative; justify-content: center; align-items: center; - flex-direction: column; - position: relative; - width: 20.5rem; margin: 1.2rem; + padding: 0 0.5rem; } .c-reality-upgrade-btn { - background-color: black; - border: 0.2rem solid var(--color-reality); - border-radius: 0.5rem; - color: var(--color-reality-light); - cursor: pointer; - transition-duration: 0.15s; text-align: center; - font-size: 1rem; font-family: Typewriter, serif; + font-size: 1rem; + color: var(--color-reality-dark); + background-color: var(--color-background); + border: var(--var-border-width, 0.2rem) solid var(--color-reality-dark); + border-radius: var(--var-border-radius, 0.5rem); + transition-duration: 0.15s; + cursor: pointer; +} + +.s-base--dark .c-reality-upgrade-btn { + color: var(--color-reality-light); } .c-reality-upgrade-btn__requirement { @@ -8664,20 +8766,21 @@ kbd { } .c-reality-upgrade-btn--unavailable { - background-color: #656565; + color: var(--color-reality-light); + background-color: var(--color-glyph-undo-disabled); cursor: default; } .c-reality-upgrade-btn--unavailable:hover { - background-color: #656565; color: var(--color-reality-light); + background-color: var(--color-glyph-undo-disabled); } .c-reality-upgrade-btn--useless { background-color: var(--color-pelle--base); border-color: #4a110b; - cursor: default; filter: grayscale(50%); + cursor: default; } .c-reality-upgrade-btn--useless:hover { @@ -8687,8 +8790,9 @@ kbd { } .c-reality-upgrade-btn--bought { + color: var(--color-reality-light); background-color: var(--color-reality); - border-color: #094E0B; + border-color: #094e0b; cursor: default; } @@ -8698,14 +8802,14 @@ kbd { } .c-reality-upgrade-btn--possible { - background-color: #8b8529; color: black; + background-color: #8b8529; cursor: default; } .c-reality-upgrade-btn--possible:hover { - background-color: #8b8529; color: black; + background-color: #8b8529; } .c-reality-upgrade-btn--locked { @@ -8714,18 +8818,13 @@ kbd { } .c-reality-upgrade-btn--locked:hover { - background-color: #a53939; color: var(--color-reality-light); + background-color: #a53939; } .c-reality-upgrade-btn--black-hole-unlock { + text-shadow: -0.1rem 0.1rem 0.3rem var(--color-reality-light); margin: auto; - text-shadow: -0.1rem 0.1rem 0.3rem var(--color-reality-light) -} - -.s-base--metro .c-reality-upgrade-btn { - border-width: 0.1rem; - border-radius: 0; } .t-dark .c-reality-upgrade-btn--unavailable { @@ -8745,14 +8844,14 @@ kbd { .t-s6.c-reality-upgrade-btn--unavailable:hover, .t-s10.c-reality-upgrade-btn--unavailable:hover { - background-color: #656565; color: var(--color-reality-light); + background-color: #656565; } .t-s6.c-reality-upgrade-btn--bought, .t-s10.c-reality-upgrade-btn--bought { background-color: var(--color-reality); - border-color: #094E0B; + border-color: #094e0b; } .t-s6.c-reality-upgrade-btn--bought:hover, @@ -8784,17 +8883,18 @@ kbd { } .c-black-hole-canvas { - margin-top: 0.5rem; border: 0.1rem solid black; + border-radius: var(--var-border-radius, 0.4rem); + margin-top: 0.5rem; } .t-s1 .c-black-hole-canvas { background: black; - border-radius: 50%; + border-radius: var(--var-border-radius, 50%); } .l-black-hole-sliders { - width: 65rem; + width: 55rem; margin: 1rem auto; } @@ -8807,20 +8907,15 @@ kbd { color: var(--color-reality); } -/*#region reality-tab*/ - -.c-reality-tab__header { - font-size: 1.5rem; - margin: 1.5rem 0; -} +/* #region reality-tab */ .c-reality-tab__reality-machines { color: var(--color-reality); } -/*#region themes*/ +/* #region themes */ -/*#region t-dark t-s6 t-s10*/ +/* #region t-dark t-s6 t-s10 */ .t-dark .c-reality-tab__reality-machines, .t-s6 .c-reality-tab__reality-machines, @@ -8828,47 +8923,41 @@ kbd { text-shadow: 0 0 0.7rem; } -/*#endregion t-dark t-s6 t-s10*/ +/* #endregion t-dark t-s6 t-s10 */ -/*#region t-s1*/ +/* #region t-s1 */ .t-s1 .c-reality-tab__reality-machines { text-shadow: 0.1rem 0.1rem 0 black; } -/*#endregion t-s1*/ +/* #endregion t-s1 */ -/*#endregion themes*/ +/* #endregion themes */ -/*#endregion reality-tab*/ - -.c-automator-blocks { - width: 100%; - padding-bottom: 5rem; -} +/* #endregion reality-tab */ .o-automator-command { - border: 0.1rem solid #353535; - padding: 0.5rem; - display: inline-block; - background: #000115; - color: #c080ff; - border-radius: 0.5rem; - width: 8.5rem; + width: fit-content; + height: 3rem; + min-width: 4.5rem; + color: var(--color-blockmator-block-command); + background: var(--color-blockmator-block-background); + border: 0.1rem solid var(--color-blockmator-block-border); + border-radius: var(--var-border-radius, 0.5rem); + padding: 0 1rem; + -webkit-user-select: none; user-select: none; cursor: grab; } -.o-automator-block-list { - display: flex; - justify-content: center; - align-items: center; - height: 5.5rem; +.s-base--dark .o-automator-command { + color: #c080ff; } .c-automator-docs .o-automator-command { - margin-bottom: 0.5rem; margin-right: 0.5rem; + margin-bottom: 0.5rem; } .o-automator-command--ghost { @@ -8877,81 +8966,88 @@ kbd { .c-automator-command-list { display: flex; - align-items: center; flex-wrap: wrap; + align-items: center; } .c-automator-block-row { display: flex; + height: 2.85rem; align-items: center; - margin-bottom: 0.5rem; + padding: 1.73rem 0; } .c-automator-block-row > * { - margin-right: 0.5rem -} - -.c-automator-block-row-active { - background: #50316f; + margin-right: 0.5rem; } .c-automator-block-row-ghost { display: block; + width: 100%; margin: auto; - margin-left: 0; margin-bottom: 0.5rem; + margin-left: 0; + padding: 0.3rem 0.6rem; } .c-automator-block-editor { overflow-y: scroll; height: 100%; - background-color: black; - border-top-left-radius: 1rem; - border-bottom-left-radius: 1rem; + background-color: var(--color-blockmator-editor-background); box-sizing: border-box; - padding: 0.5rem; - tab-size: 1.5em; - -moz-tab-size: 1.5em; - -o-tab-size: 1.5em; +} + +.s-base--metro .c-automator-block-editor::-webkit-scrollbar-thumb { + border-radius: 0; } .o-automator-block-input { - border: 0.1rem solid #353535; - padding: 0.5rem; display: inline-block; - background: #000115; - color: #007f7f; - border-radius: 0.5rem; - font-size: 1.1rem; + width: fit-content; font-family: Typewriter, serif; + font-size: 1.1rem; + background: var(--color-blockmator-block-background); + border: 0.1rem solid var(--color-blockmator-block-border); + border-radius: var(--var-border-radius, 0.5rem); + margin-right: 0.3rem; + padding: 0.5rem; +} + +.s-base--dark .o-automator-block-input { + color: #01a1a1; } input.o-automator-block-input { - color: #0f0; + width: 10rem; + color: var(--color-blockmator-block-command); } .l-automator-nested-block { width: fit-content; - padding: 1rem; - border: 2px dotted #55ff55; - margin-left: 3rem; + min-width: 30rem; + border: var(--var-border-width, 2px) dotted #369b36; margin-top: 0.5rem; - min-widtH: 30rem; + margin-left: 3rem; + padding: 1rem; +} + +.s-base--dark .l-automator-nested-block { + border: var(--var-border-width, 2px) dotted #55ff55; } .o-automator-block-delete { - color: #ff3333; font-size: 1.7rem; + color: #ff3333; cursor: pointer; } .c-automator-template-container { display: flex; flex-direction: column; + width: 70rem; + height: 65rem; justify-content: space-between; margin: 1rem; - height: 65rem; - width: 70rem; } .c-automator-template-description { @@ -8971,9 +9067,9 @@ input.o-automator-block-input { } .c-automator-template-textbox { - border: 0.1rem solid black; - border-radius: 0.3rem; text-align: center; + border: 0.1rem solid black; + border-radius: var(--var-border-radius, 0.3rem); } .c-automator-template-textbox--preset { @@ -8987,46 +9083,51 @@ input.o-automator-block-input { .c-perk-tab { display: flex; flex-direction: column; - align-items: center; position: relative; + align-items: center; } .c-perk-network { - position: relative; overflow: hidden; - touch-action: pan-y; + position: relative; + border: var(--var-border-width, 0.2rem) solid black; + border-radius: var(--var-border-radius, 0.4rem); + -webkit-user-select: none; user-select: none; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0%); + touch-action: pan-y; -webkit-user-drag: none; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - width: 60rem; - height: 60rem; - border: 0.2rem solid black; } .c-perk-network__canvas { - position: relative; - touch-action: none; - user-select: none; - -webkit-user-drag: none; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); width: 100%; height: 100%; + position: relative; + -webkit-user-select: none; + user-select: none; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0%); + touch-action: none; + -webkit-user-drag: none; } -/* #startregion h2p tab*/ +/* #startregion h2p tab */ .l-h2p-modal { display: flex; flex-direction: column; - margin: 0.5rem; + /* stylelint-disable-next-line unit-allowed-list */ width: calc(100vw - 20vh); + /* stylelint-disable-next-line unit-allowed-list */ height: calc(100vh - 20vh); + margin: 0.5rem; } .l-h2p-container { display: flex; - flex-direction: row; flex: 1 1 50rem; + flex-direction: row; margin: 2rem 0; } @@ -9040,15 +9141,17 @@ input.o-automator-block-input { flex-direction: column; width: 15rem; } + .l-h2p-info { - flex: 1 1 50rem; display: flex; + flex: 1 1 50rem; flex-direction: column; } .c-h2p-title { - user-select: none; font-size: 3rem; + -webkit-user-select: none; + user-select: none; } .c-h2p-body { @@ -9057,9 +9160,9 @@ input.o-automator-block-input { } .l-h2p-body { - flex: 0.8 0.8 30rem; - overflow-y: scroll; - margin: 1rem; + overflow-y: auto; + flex: 1 1 30rem; + margin: 1rem 1rem 0; padding: 0.5rem; } @@ -9068,8 +9171,11 @@ input.o-automator-block-input { } .l-h2p-body::-webkit-scrollbar-thumb { - background: grey; - border-radius: 0.5rem; + border: none; +} + +.s-base--metro .l-h2p-body::-webkit-scrollbar-thumb { + border-radius: 0; } .c-h2p-body--title { @@ -9077,18 +9183,19 @@ input.o-automator-block-input { } .c-h2p-search-bar { - font-size: 1.5rem; width: calc(100% - 1rem); + font-size: 1.5rem; padding: 0.2rem; } .l-h2p-tab-list { display: flex; - justify-content: flex-start; - flex-direction: column; overflow-y: auto; - margin: 0.5rem; flex: 1 0.8 40rem; + flex-direction: column; + justify-content: flex-start; + scrollbar-width: thin; + margin: 0.5rem 0.5rem 0; } .l-h2p-tab-list::-webkit-scrollbar { @@ -9096,52 +9203,43 @@ input.o-automator-block-input { } .l-h2p-tab-list::-webkit-scrollbar-thumb { - background: grey; - border-radius: 0.5rem; + border: none; +} + +.s-base--metro .l-h2p-tab-list::-webkit-scrollbar-thumb { + border-radius: 0; } .o-h2p-tab-button { font-size: 1.25rem; - cursor: pointer; border-bottom: 0.1rem solid black; padding: 0.3rem 0.5rem 0.3rem 0; - transition: font-size 0.2s; + transition: all 0.2s; + cursor: pointer; } -.o-h2p-tab-button:hover { - color: white; - text-shadow: - 0.1rem 0.1rem 0.1rem black, - -0.1rem -0.1rem 0.1rem black, - -0.1rem 0.1rem 0.1rem black, - 0.1rem -0.1rem 0.1rem black; +.o-h2p-tab-button:hover, +.o-h2p-tab-button--selected { + box-shadow: inset 0 0 0.8rem var(--color-text); } .s-base--dark .o-h2p-tab-button { border-bottom: 0.1rem solid white; } -.o-h2p-tab-button--selected { - font-size: 1.5rem; - color: white; - text-shadow: - 0.1rem 0.1rem 0.1rem black, - -0.1rem -0.1rem 0.1rem black, - -0.1rem 0.1rem 0.1rem black, - 0.1rem -0.1rem 0.1rem black; -} - .l-help-me { + width: 2rem; + height: 2rem; position: absolute; top: 1rem; - width: 2rem; right: 2rem; - height: 2rem; - border-radius: 50%; - line-height: 1.9rem; + z-index: 1; font-size: 1.5rem; - cursor: pointer; font-weight: 500; + line-height: 1.9rem; + border-radius: var(--var-border-radius, 50%); + pointer-events: auto; + cursor: pointer; } .l-information { @@ -9155,44 +9253,37 @@ input.o-automator-block-input { .l-hide-modal-subtab-container { display: flex; flex-wrap: wrap; - width: 70rem; - justify-content: center; + width: 61rem; position: relative; + justify-content: center; } .c-hide-modal-all-subtab-container { - border-radius: 0.8rem; border: 0.1rem solid black; + border-radius: var(--var-border-radius, 0.8rem); padding: 0.2rem; cursor: pointer; } -.s-base--metro .c-hide-modal-all-subtab-container { - border-radius: 0; -} - -.s-base--metro .c-hide-modal-tab-button { - border-radius: 0; -} - .c-hide-modal-tab-button { - color: var(--color-text); - background-color: var(--color-base); - font: 1rem Typewriter, serif; display: flex; + white-space: normal; + width: 12rem; + height: 3.4rem; justify-content: center; align-items: center; - height: 3.4rem; - width: 12rem; - margin: 0.2rem; - border: 0.1rem solid black; - border-radius: 0.4rem; - box-shadow: none; - white-space: normal; + font: 1rem Typewriter, serif; font-weight: bold; - cursor: pointer; - transition-duration: 0.2s; + color: var(--color-text); + background-color: var(--color-text-inverted); + border: 0.1rem solid black; + border-radius: var(--var-border-radius, 0.4rem); + box-shadow: none; + margin: 0.2rem; padding: 0.3rem 1rem; + transition-duration: 0.2s; + user-select: none; + cursor: pointer; } .c-hide-modal-tab-button:hover { @@ -9221,54 +9312,48 @@ input.o-automator-block-input { .c-hide-modal-tab-button--infinity { color: var(--color-infinity); - background: var(--color-prestige--accent); border-color: var(--color-infinity); } .c-hide-modal-tab-button--infinity:hover { - color: var(--color-prestige--accent); background: var(--color-infinity); } .c-hide-modal-tab-button--eternity { color: var(--color-eternity); - background: var(--color-prestige--accent); - border-color: var(--color-eternity) + border-color: var(--color-eternity); } .c-hide-modal-tab-button--eternity:hover { - color: var(--color-prestige--accent); background: var(--color-eternity); } .c-hide-modal-tab-button--reality { color: var(--color-reality); - background: var(--color-prestige--accent); border-color: var(--color-reality); } .c-hide-modal-tab-button--reality:hover { - color: var(--color-prestige--accent); background: var(--color-reality); } .c-hide-modal-tab-button--celestials { color: var(--color-celestials); - background: var(--color-prestige--accent); border-color: var(--color-celestials); } .c-hide-modal-tab-button--celestials:hover { - color: var(--color-prestige--accent); background: var(--color-celestials); } /* Prevent any themes overriding font awesome */ -.fa, .far, .fas { - font-family: "Font Awesome 5 Free" !important; +.fa, +.far, +.fas { + font-family: "Font Awesome 6 Free" !important; } -@keyframes opacity { +@keyframes a-opacity { 0% { opacity: 0; } 50% { opacity: 0.4; } 100% { opacity: 0; } @@ -9278,30 +9363,35 @@ input.o-automator-block-input { position: relative; } -.tutorial--glow:after { +.tutorial--glow::after { content: ""; - position: absolute; width: 100%; height: 100%; - background: gold; - animation: opacity 3s infinite; + position: absolute; top: 0; left: 0; - border-radius: inherit; + background: gold; + border-radius: var(--var-border-radius, inherit); + animation: a-opacity 3s infinite; +} + +.o-celestial-nav__hoverable .tooltiptext { + display: none; } -.o-celestial-nav__hoverable .tooltiptext { display: none; } .o-celestial-nav__hoverable:hover .tooltiptext, -.o-celestial-nav__force-hover .tooltiptext { display: block; } +.o-celestial-nav__force-hover .tooltiptext { + display: block; +} .c-celestial-nav__test-ring { fill: blue; } .c-celestial-nav__test-ring:hover { - fill: red; stroke: black; stroke-width: 2; + fill: red; } .c-celestial-nav__test-complete { @@ -9313,41 +9403,45 @@ input.o-automator-block-input { } .c-celestial-nav__test-incomplete { - fill: #888; + fill: #888888; +} + +.c-celestial-nav__drained-rift { + fill: #111111; } .o-celestial-nav__symbol { text-anchor: middle; + font-family: "Font Awesome", Typewriter; font-weight: bold; - font-family: Font Awesome; } .o-celestial-nav__legend-arrow { - stroke: #CCC; - fill: none; - stroke-linecap: butt; + stroke: #cccccc; stroke-width: 2; + stroke-linecap: butt; + fill: none; } .o-celestial-nav__hoverable:hover .o-celestial-nav__legend-arrow { - stroke: #FFF; + stroke: #ffffff; } .o-celestial-nav__legend-outline { stroke: none; - fill: none; - stroke-linecap: butt; stroke-width: 2; + stroke-linecap: butt; + fill: none; } .o-celestial-nav__hoverable:hover .o-celestial-nav__legend-outline { - stroke: #FFF; + stroke: #ffffff; } .o-celestial-nav__node-overlay { stroke: transparent; - fill: transparent; stroke-width: 12; + fill: transparent; } .o-celestial-nav__legend-text { @@ -9361,41 +9455,36 @@ input.o-automator-block-input { } .o-no-mouse { - -moz-user-select: none; - -khtml-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; pointer-events: none; + -webkit-user-select: none; + user-select: none; } .tooltiptext { - -moz-user-select: none; - -khtml-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; pointer-events: none; + -webkit-user-select: none; + user-select: none; } .l-celestial-navigation { + border: var(--var-border-width, 0.2rem) solid black; + border-radius: var(--var-border-radius, 5px); margin: auto; - border: 2px solid black; - border-radius: 5px; + /* background: #1a1a1a; */ } .c-autobuyer-buy-box { display: flex; + width: 90rem; + height: 6.4rem; justify-content: center; align-items: center; - height: 6.4rem; font-size: 1.5rem; - border-radius: 0.5rem; - border-width: 0.2rem; + border-width: var(--var-border-width, 0.2rem); + border-radius: var(--var-border-radius, 0.5rem); margin: 0.5rem; padding: 1rem; - width: 90rem; } .t-s6 .c-autobuyer-buy-box, @@ -9409,20 +9498,42 @@ input.o-automator-block-input { height: 6.2rem; } -.c-pelle-useless--available { - color: var(--color-pelle-secondary); +.o-pelle-disabled { + text-decoration: line-through; } -.c-pelle-useless--available:hover { - filter: grayscale(50%); +.o-pelle-disabled-pointer { + cursor: not-allowed; } -.c-pelle-useless--bought { - background-color: var(--color-pelle--base); - filter: grayscale(50%); +.o-pelle-disabled-pointer:active { + pointer-events: none; +} + +.c-pelle-panel-title { + position: relative; + font-size: 3rem; + font-weight: bold; + color: var(--color-pelle--base); +} + +.l-pelle-content-container { + display: flex; + flex-direction: column; + align-items: center; +} + +.l-pelle-panel-container { + width: 98rem; + border: var(--var-border-width, 0.2rem) solid var(--color-pelle--base); + border-radius: var(--var-border-radius, 0.5rem); + margin: 1rem; + padding: 1rem; + -webkit-user-select: none; + user-select: none; } .l-fill-container { width: 100%; height: 100%; -} \ No newline at end of file +} diff --git a/public/stylesheets/theme-Dark Metro.css b/public/stylesheets/theme-Dark Metro.css index ace7ff67b..355f0b0af 100644 --- a/public/stylesheets/theme-Dark Metro.css +++ b/public/stylesheets/theme-Dark Metro.css @@ -1,30 +1,26 @@ body.t-dark-metro { - background-image: url('../images/dark-bg.png'); - background-position: center; color: #757575; + background-image: url("../images/dark-bg.png"); + background-position: center; } .t-dark-metro .c-tt-buy-button--unlocked { + color: #00bcd4; background: #212121; - color: #00BCD4; - border: 0.1rem solid #00BCD4; + border: 0.1rem solid #00bcd4; } .t-dark-metro .c-tt-buy-button--unlocked:hover { color: #212121; - background: #00BCD4; + background: #00bcd4; } .t-dark-metro .c-tt-buy-button--locked { - background: #9E9E9E; + background: #9e9e9e; border: none; - box-shadow: 1px 1px 1px 0 #000; + box-shadow: 1px 1px 1px 0 #000000; } .t-dark-metro .c-tt-buy-button--locked:hover { - background: #E53935; -} - -.t-dark-metro #realityanimbg { - filter: invert(1); -} + background: #e53935; +} \ No newline at end of file diff --git a/public/stylesheets/theme-Dark.css b/public/stylesheets/theme-Dark.css index 16cb82813..0e5da459d 100644 --- a/public/stylesheets/theme-Dark.css +++ b/public/stylesheets/theme-Dark.css @@ -1,33 +1,33 @@ body.t-dark { - background-image: url('../images/dark-bg.png'); - background-position: center; color: #888888; + background-image: url("../images/dark-bg.png"); + background-position: center; } input.t-dark { - background-color: #455A64; + background-color: #455a64; border: 0.1rem solid black; } .t-dark .c-tt-amount { - color: #64DD17; - text-shadow: 0 0 7px #64DD17; + color: #64dd17; + text-shadow: 0 0 7px #64dd17; } .t-dark .c-tt-buy-button--unlocked { - color: #3AACD6; + color: #3aacd6; background: #161616; - border: 0.1rem solid #3AACD6; + border: 0.1rem solid #3aacd6; } .t-dark .c-tt-buy-button--unlocked:hover { color: #161616; - background: #3AACD6; + background: #3aacd6; } .t-dark .c-tt-buy-button--locked { background: #263238; - border: 0.1rem solid #3AACD6; + border: 0.1rem solid #3aacd6; } .t-dark .c-tt-buy-button--locked:hover { @@ -37,8 +37,4 @@ input.t-dark { .t-dark .c-rm-amount__desc { color: #757575; -} - -.t-dark #realityanimbg { - filter: invert(1); } \ No newline at end of file diff --git a/public/stylesheets/theme-Inverted Metro.css b/public/stylesheets/theme-Inverted Metro.css index 5f40eb0e0..06218b9ff 100644 --- a/public/stylesheets/theme-Inverted Metro.css +++ b/public/stylesheets/theme-Inverted Metro.css @@ -4,10 +4,7 @@ body.t-inverted-metro { .t-inverted-metro #container, .t-inverted-metro #page, -.t-inverted-metro .l-notification-container, -.t-inverted-metro .c-modal, -.t-inverted-metro #TTbuttons, -.t-inverted-metro .sidebar, +.t-inverted-metro #ui-fixed, .t-inverted-metro .c-glyph-tooltip { filter: invert(100%); } @@ -18,28 +15,28 @@ body.t-inverted-metro { .t-inverted-metro #timetheorems, .t-inverted-metro .c-ttshop__save-load-text, -.t-inverted-metro #minimizeArrow, +.t-inverted-metro .minimize-arrow, .t-inverted-metro .ttshop-background { - color: #bbb; + color: #bbbbbb; } .t-inverted-metro .c-tt-buy-button--unlocked { + color: #00bcd4; background: #212121; - color: #00BCD4; - border: 0.1rem solid #00BCD4; + border: 0.1rem solid #00bcd4; } .t-inverted-metro .c-tt-buy-button--unlocked:hover { color: #212121; - background: #00BCD4; + background: #00bcd4; } .t-inverted-metro .c-tt-buy-button--locked { - background: #9E9E9E; + background: #9e9e9e; border: none; - box-shadow: 1px 1px 1px 0 #000; + box-shadow: 1px 1px 1px 0 #000000; } .t-inverted-metro .c-tt-buy-button--locked:hover { - background: #EF5350; + background: #ef5350; } diff --git a/public/stylesheets/theme-Inverted.css b/public/stylesheets/theme-Inverted.css index d390c7b16..611c95f7c 100644 --- a/public/stylesheets/theme-Inverted.css +++ b/public/stylesheets/theme-Inverted.css @@ -4,10 +4,7 @@ body.t-inverted { .t-inverted #container, .t-inverted #page, -.t-inverted .l-notification-container, -.t-inverted .c-modal, -.t-inverted #TTbuttons, -.t-inverted .sidebar, +.t-inverted #ui-fixed, .t-inverted .c-glyph-tooltip { filter: invert(100%); } diff --git a/public/stylesheets/theme-Metro.css b/public/stylesheets/theme-Metro.css index 58ed41035..97e8ab2f3 100644 --- a/public/stylesheets/theme-Metro.css +++ b/public/stylesheets/theme-Metro.css @@ -1,20 +1,20 @@ .t-metro .c-tt-buy-button--unlocked { + color: #00bcd4; background: #212121; - color: #00BCD4; - border: 0.1rem solid #00BCD4; + border: 0.1rem solid #00bcd4; } .t-metro .c-tt-buy-button--unlocked:hover { color: #212121; - background: #00BCD4; + background: #00bcd4; } .t-metro .c-tt-buy-button--locked { - background: #9E9E9E; + background: #9e9e9e; border: none; - box-shadow: 1px 1px 1px 0 #000; + box-shadow: 1px 1px 1px 0 #000000; } .t-metro .c-tt-buy-button--locked:hover { - background: #EF5350; -} \ No newline at end of file + background: #ef5350; +} diff --git a/public/stylesheets/theme-S1.css b/public/stylesheets/theme-S1.css index ccbeb4c19..bb429bf59 100644 --- a/public/stylesheets/theme-S1.css +++ b/public/stylesheets/theme-S1.css @@ -1,11 +1,10 @@ -#ui-container { - background: url('../images/s1-bg.svg') no-repeat; - background-size: contain; +html { align-content: center; - background-size: 100%; + background: url("../images/s1-bg.svg") no-repeat; + background-attachment: fixed; background-color: #d72621; - background-attachment:fixed; - background-position:center bottom; + background-position: center bottom; + background-size: 100%; } body.t-s1 { @@ -38,26 +37,26 @@ body.t-s1 { #snow { display: block !important; - background-image: url('../images/snow1.png'), url('../images/snow2.png'), url('../images/snow3.png'); - height: 100%; width: 100%; - left: 0; + height: 100%; + position: fixed; top: 0; - position: fixed; + left: 0; z-index: 0; opacity: 1; + background-image: url("../images/snow1.png"), url("../images/snow2.png"), url("../images/snow3.png"); + animation: a-snow 10s linear infinite, a-snow-fade 10s cubic-bezier(0, 0.3, 1, 0.7) infinite; pointer-events: none; - animation: snow 10s linear infinite, snowFade 10s cubic-bezier(0,0.3,1,0.7) infinite; } -@keyframes snow { - 0% {background-position: 0 0, 0 0, 0 0;} - 50% {background-position: 500px 500px, 100px 200px, -100px 150px;} - 100% {background-position: 1000px 1000px, 200px 400px, -200px 300px;} +@keyframes a-snow { + 0% { background-position: 0 0, 0 0, 0 0; } + 50% { background-position: 500px 500px, 100px 200px, -100px 150px; } + 100% { background-position: 1000px 1000px, 200px 400px, -200px 300px; } } -@keyframes snowFade { - 0% {opacity: 0;} - 50% {opacity: 1;} - 100% {opacity: 0;} +@keyframes a-snow-fade { + 0% { opacity: 0; } + 50% { opacity: 1; } + 100% { opacity: 0; } } diff --git a/public/stylesheets/theme-S10.css b/public/stylesheets/theme-S10.css index 0a2c3bec7..4fdcb678b 100644 --- a/public/stylesheets/theme-S10.css +++ b/public/stylesheets/theme-S10.css @@ -1,43 +1,44 @@ .t-s10 #stars { display: block; - /* Make video to at least 100% wide and tall */ - min-width: 100%; - min-height: 100%; /* Setting width & height to auto prevents the browser from stretching or squishing the video */ width: auto; height: auto; + /* Make video to at least 100% wide and tall */ + min-width: 100%; + min-height: 100%; + /* Center the video */ position: absolute; top: 50%; left: 50%; z-index: -1000; - transform: translate(-50%,-50%); + transform: translate(-50%, -50%); } body.t-s10 { - color: #999; + color: #999999; background: black; - background-image: url('../images/stars-bg.png'); - background-size: 100%; + background-image: url("../images/stars-bg.png"); background-position: center; + background-size: 100%; } .t-s10 .c-tt-amount { - color: #64DD17; - text-shadow: 0 0 7px #64DD17; + color: #64dd17; + text-shadow: 0 0 7px #64dd17; } .t-s10 .c-tt-buy-button--unlocked { - color: #3AACD6; + color: #3aacd6; background: black; - border: 0.1rem solid #3AACD6; + border: 0.1rem solid #3aacd6; } .t-s10 .c-tt-buy-button--unlocked:hover { color: black; - background: #3AACD6; + background: #3aacd6; } .t-s10 .c-tt-buy-button--locked { @@ -53,9 +54,10 @@ body.t-s10 { } .t-s10 .c-rm-amount__desc { - color: #999; + color: #999999; } -.t-s10 #realityanimbg { - filter: invert(1); +.t-s10 .c-alchemy-resource-info { + color: white; + border-color: white; } diff --git a/public/stylesheets/theme-S11.css b/public/stylesheets/theme-S11.css index aa2169d99..2d07d0a3a 100644 --- a/public/stylesheets/theme-S11.css +++ b/public/stylesheets/theme-S11.css @@ -1,29 +1,29 @@ @font-face { font-family: Typewriter; - src: url('BlobEmoji-Bold.ttf'), url('MonospaceTypewriter.ttf'); + src: url("BlobEmoji-Bold.ttf"), url("MonospaceTypewriter.ttf"); } body.t-s11 { - color: #999; + color: #999999; background: black; - background-size: 100%; background-position: center; + background-size: 100%; } .t-s11 .c-tt-amount { - color: #64DD17; - text-shadow: 0 0 7px #64DD17; + color: #64dd17; + text-shadow: 0 0 7px #64dd17; } .t-s11 .c-tt-buy-button--unlocked { - color: #3AACD6; + color: #3aacd6; background: black; - border: 0.1rem solid #3AACD6; + border: 0.1rem solid #3aacd6; } .t-s11 .c-tt-buy-button--unlocked:hover { color: black; - background: #3AACD6; + background: #3aacd6; } .t-s11 .c-tt-buy-button--locked { @@ -39,11 +39,7 @@ body.t-s11 { } .t-s11 .c-rm-amount__desc { - color: #999; -} - -.t-s11 #realityanimbg { - filter: invert(1); + color: #999999; } .t-s11 .c-modal { @@ -51,11 +47,17 @@ body.t-s11 { box-shadow: 0 0 1.5rem 0 black; } +.t-s11 .c-modal__confirmation-toggle__checkbox { + top: 0.05rem; + border-color: black; +} + .o-tachyon-particle { + fill: orange !important; font-family: Typewriter; font-size: 20px; - fill: orange !important; pointer-events: none; + -webkit-user-select: none; user-select: none; cursor: default; } diff --git a/public/stylesheets/theme-S2.css b/public/stylesheets/theme-S2.css index a6a0ccc46..b7c59b0f4 100644 --- a/public/stylesheets/theme-S2.css +++ b/public/stylesheets/theme-S2.css @@ -1,10 +1,9 @@ html { - background: url('../images/s2-bg.svg') no-repeat; - background-size: contain; + background: url("../images/s2-bg.svg") no-repeat; + background-color: white; background-position-x: 50%; background-position-y: 50%; background-size: 50%; - background-color: white; } body { @@ -17,8 +16,8 @@ body { .t-s2 #page, .t-s2 .l-notification-container, .t-s2 .c-modal, -.t-s2 #TTbuttons, -.t-s2 .sidebar, +.t-s2 .time-theorem-buttons, +.t-s2 .c-modern-sidebar, .t-s2 .c-glyph-tooltip { filter: sepia(100%) hue-rotate(180deg) saturate(250%); } diff --git a/public/stylesheets/theme-S3.css b/public/stylesheets/theme-S3.css index 9295871b0..c861147a3 100644 --- a/public/stylesheets/theme-S3.css +++ b/public/stylesheets/theme-S3.css @@ -2,28 +2,24 @@ body { background: none !important; } -.t-s3 #container, -.t-s3 #page, -.t-s3 .l-notification-container, -.t-s3 .c-modal, -.t-s3 #TTbuttons, -.t-s3 .sidebar, +.t-s3 #ui, +.t-s3 #ui-fixed, .t-s3 .c-glyph-tooltip { - animation: glasses 7s infinite; + animation: a-glasses 7s infinite; } -@keyframes glasses { - 0% {filter: blur(0);} - 10% {filter: blur(3px);} - 20% {filter: blur(0);} - 30% {transform: rotateZ(0deg)} - 35% {transform: rotateZ(4deg)} - 40% {transform: rotateZ(-4deg)} - 45% {transform: rotateZ(0deg)} - 55% {filter: invert(0%);} - 65% {filter: invert(100%);} - 75% {filter: invert(0%);} - 85% {filter: opacity(100%);} - 92.5% {filter: opacity(50%);} - 100% {filter: opacity(100%);} +@keyframes a-glasses { + 0% { filter: blur(0); } + 10% { filter: blur(3px); } + 20% { filter: blur(0); } + 30% { transform: rotateZ(0deg); } + 35% { transform: rotateZ(4deg); } + 40% { transform: rotateZ(-4deg); } + 45% { transform: rotateZ(0deg); } + 55% { filter: invert(0%); } + 65% { filter: invert(100%); } + 75% { filter: invert(0%); } + 85% { filter: opacity(100%); } + 92.5% { filter: opacity(50%); } + 100% { filter: opacity(100%); } } diff --git a/public/stylesheets/theme-S4.css b/public/stylesheets/theme-S4.css index f776fbfac..a64422f09 100644 --- a/public/stylesheets/theme-S4.css +++ b/public/stylesheets/theme-S4.css @@ -1,32 +1,34 @@ * { + font-family: + "Comic Sans MS", + cursive, + sans-serif, + Typewriter, + fas, + fab, + "Font Awesome 6 Brands", + "Font Awesome 6 Free" !important; border-radius: 40% !important; + box-shadow: 1px 1px 1px 0 #9e9e9e; } -* { - font-family: "Comic Sans MS", cursive, sans-serif !important; -} - -html{ - background-color: #FF00FF; +html { color: black !important; - background-attachment:fixed; - background-position:center bottom; + background-attachment: fixed; + background-color: #ff00ff; + background-position: center bottom; border: 0.1rem solid white !important; } #container { - cursor: url(cursor.cur), auto !important; + cursor: url("cursor.cur"), auto !important; } body { - background: #0FFF0F !important; + background: #0fff0f !important; border: 0.1rem solid white !important; } .o-achievement { border-radius: 0.8rem !important; } - -* { - box-shadow: 1px 1px 1px 0 #9E9E9E; -} diff --git a/public/stylesheets/theme-S5.css b/public/stylesheets/theme-S5.css index b7095cc35..6f7ef5d3b 100644 --- a/public/stylesheets/theme-S5.css +++ b/public/stylesheets/theme-S5.css @@ -1,5 +1,5 @@ html { - background: url('../images/s5-bg.jpg'); + background: url("../images/s5-bg.jpg"); background-position-x: 50%; background-position-y: 50%; background-size: 150%; @@ -15,7 +15,7 @@ body.t-s5 { .t-s5 #page, .t-s5 .l-notification-container, .t-s5 .c-modal, -.t-s5 #TTbuttons, -.t-s5 .sidebar { +.t-s5 .time-theorem-buttons, +.t-s5 .c-modern-sidebar { filter: sepia(100%) hue-rotate(0deg) saturate(100%); } diff --git a/public/stylesheets/theme-S6.css b/public/stylesheets/theme-S6.css index 1a3d61e37..cf7425be4 100644 --- a/public/stylesheets/theme-S6.css +++ b/public/stylesheets/theme-S6.css @@ -1,43 +1,44 @@ .t-s6 #stars { display: block; - /* Make video to at least 100% wide and tall */ - min-width: 100%; - min-height: 100%; /* Setting width & height to auto prevents the browser from stretching or squishing the video */ width: auto; height: auto; + /* Make video to at least 100% wide and tall */ + min-width: 100%; + min-height: 100%; + /* Center the video */ position: absolute; top: 50%; left: 50%; z-index: -1000; - transform: translate(-50%,-50%); + transform: translate(-50%, -50%); } body.t-s6 { - color: #999; + color: #999999; background: black; - background-image: url('../images/stars-bg.png'); - background-size: 100%; + background-image: url("../images/stars-bg.png"); background-position: center; + background-size: 100%; } .t-s6 .c-tt-amount { - color: #64DD17; - text-shadow: 0 0 7px #64DD17; + color: #64dd17; + text-shadow: 0 0 7px #64dd17; } .t-s6 .c-tt-buy-button--unlocked { - color: #3AACD6; + color: #3aacd6; background: black; - border: 0.1rem solid #3AACD6; + border: 0.1rem solid #3aacd6; } .t-s6 .c-tt-buy-button--unlocked:hover { color: black; - background: #3AACD6; + background: #3aacd6; } .t-s6 .c-tt-buy-button--locked { @@ -53,9 +54,10 @@ body.t-s6 { } .t-s6 .c-rm-amount__desc { - color: #999; + color: #999999; } -.t-s6 #realityanimbg { - filter: invert(1); +.t-s6 .c-alchemy-resource-info { + color: white; + border-color: white; } diff --git a/public/stylesheets/theme-S7.css b/public/stylesheets/theme-S7.css index d58c41386..53adb67aa 100644 --- a/public/stylesheets/theme-S7.css +++ b/public/stylesheets/theme-S7.css @@ -1,40 +1,33 @@ * { - font-family: "Arial"; - transition-duration: 1ms !important; + font-family: Arial, Typewriter; border-width: 1px !important; border-radius: 0 !important; box-shadow: none !important; + transition-duration: 1ms !important; animation: none !important; } -#ui { +#ui, +#ui-fixed { filter: saturate(0) !important; } button { - -webkit-border-radius: 0 !important; - -moz-border-radius: 0 !important; - border-radius: 0 !important; - background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.00, rgb(222, 222, 222)), color-stop(1.00, rgb(246, 246, 246))) !important; - background-image: -moz-linear-gradient(center bottom, rgb(222, 222, 222) 0%, rgb(246, 246, 246) 100%) !important; - background-image: linear-gradient(to top, rgb(222, 222, 222) 0%, rgb(246, 246, 246) 100%) !important; - padding: 3px !important; - border: 0.1rem solid #9B9B9B !important; color: black !important; text-decoration: none !important; + background-image: linear-gradient(to top, rgb(222, 222, 222) 0%, rgb(246, 246, 246) 100%) !important; + border: 0.1rem solid #9b9b9b !important; + border-radius: 0 !important; + padding: 3px !important; } .o-primary-btn { - -webkit-border-radius: 0 !important; - -moz-border-radius: 0 !important; - border-radius: 0 !important; - background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.00, rgb(222, 222, 222)), color-stop(1.00, rgb(246, 246, 246))) !important; - background-image: -moz-linear-gradient(center bottom, rgb(222, 222, 222) 0%, rgb(246, 246, 246) 100%) !important; - background-image: linear-gradient(to top, rgb(222, 222, 222) 0%, rgb(246, 246, 246) 100%) !important; - padding: 3px !important; - border: 0.1rem solid #9B9B9B !important; color: black !important; text-decoration: none !important; + background-image: linear-gradient(to top, rgb(222, 222, 222) 0%, rgb(246, 246, 246) 100%) !important; + border: 0.1rem solid #9b9b9b !important; + border-radius: 0 !important; + padding: 3px !important; } button:hover { @@ -42,8 +35,8 @@ button:hover { } .c-glyph-tooltip { - filter: saturate(0); text-shadow: none !important; + filter: saturate(0); } .c-glyph-tooltip__effect span { diff --git a/public/stylesheets/theme-S8.css b/public/stylesheets/theme-S8.css index db5515b0c..7663ebd6f 100644 --- a/public/stylesheets/theme-S8.css +++ b/public/stylesheets/theme-S8.css @@ -1,44 +1,44 @@ body.t-s8 { - background: url('../images/s8-bg.jpg') no-repeat; - color: black; - background-color: white; height: 100%; + color: black; + background: url("../images/s8-bg.jpg") no-repeat; + background-attachment: fixed; + background-color: white; /* Center and scale the image nicely */ background-position: center; background-repeat: no-repeat; background-size: cover; - background-attachment: fixed; } .t-s8 .container { color: black; } -/*metro theme with less box shadows*/ +/* metro theme with less box shadows */ - -.t-s8 input, .t-s8 select { - border: 0.1rem solid #A9A9A9; +.t-s8 input, +.t-s8 select { + border: 0.1rem solid #a9a9a9; } .t-s8 .c-tt-buy-button--unlocked { + color: #00bcd4; background: #212121; - color: #00BCD4; - border: 0.1rem solid #00BCD4; + border: 0.1rem solid #00bcd4; } .t-s8 .c-tt-buy-button--unlocked:hover { color: #212121; - background: #00BCD4; + background: #00bcd4; } .t-s8 .c-tt-buy-button--locked { - background: #9E9E9E; + background: #9e9e9e; border: none; - box-shadow: 1px 1px 1px 0 #000; + box-shadow: 1px 1px 1px 0 #000000; } .t-s8 .c-tt-buy-button--locked:hover { - background: #EF5350; + background: #ef5350; } diff --git a/public/stylesheets/theme-S9.css b/public/stylesheets/theme-S9.css index 750be9d82..ecbfdeb9c 100644 --- a/public/stylesheets/theme-S9.css +++ b/public/stylesheets/theme-S9.css @@ -1,10 +1,38 @@ * { color: transparent !important; text-shadow: none !important; + -webkit-user-select: none; user-select: none; } -[ach-tooltip]:before { +/* These are needed for the shop not to be blind */ +/* new UI */ +.c-shop-header > span, +.o-shop-button-description, +.o-shop-button-multiplier, +.c-shop-modal .c-modal__title, +.c-shop-modal .c-modal__close-btn, +.o-modal-store-label { + color: white !important; +} + +.c-shop-header > button, +.o-shop-button-button, +.o-modal-store-btn, +.o-modal-store-btn > span, +.c-shop-disclaimer { + color: black !important; +} + +/* classic UI */ +.l-old-ui .c-shop-header > span, +.old-ui .c-shop-modal .c-modal__title, +.old-ui .o-modal-store-label, +.old-ui .c-shop-modal .c-modal__close-btn { + color: black !important; +} + +[ach-tooltip]::before { color: transparent; } @@ -23,19 +51,23 @@ color: transparent !important; } -@keyframes a-reality-glyph-icon-cycle { - 0% { color: transparent; } +.o-celestial-nav__symbol { + display: none; +} + +.c-effarig-run-button__inner--running { + display: none; +} + +.o-laitela-run-button__icon, +.o-laitela-run-button__icon:hover { + background-image: none; } @keyframes a-reality-glyph-description-cycle { 0% { color: transparent; } } -@keyframes a-reality-glyph-dot-cycle { - from { background: none; } - to { background: none; } -} - .o-celestial-nav__legend-text { fill: transparent !important; -} \ No newline at end of file +} diff --git a/public/stylesheets/time-studies.css b/public/stylesheets/time-studies.css index b50fdac7e..3fb892b43 100644 --- a/public/stylesheets/time-studies.css +++ b/public/stylesheets/time-studies.css @@ -1,8 +1,8 @@ .l-time-studies-tab { display: flex; flex-direction: column; - align-items: center; position: relative; + align-items: center; } .l-time-studies-tab__tree { @@ -30,23 +30,23 @@ .l-time-study-connection { position: absolute; - left: 0; top: 0; + left: 0; z-index: 0; } -/*#region o-time-study*/ +/* #region o-time-study */ .c-pelle-useless { - border: 0.1rem solid; - background-color: crimson; - font-weight: bold; - font-family: Typewriter, serif; width: 18rem; height: 10rem; + font-family: Typewriter, serif; + font-size: 1rem; + font-weight: bold; + background-color: crimson; + border: 0.1rem solid; + border-radius: var(--var-border-radius, 0.4rem); transition-duration: 0.2s; - border-radius: 0.4rem; - font-size: 1.0rem; } .c-pelle-useless-available { @@ -55,15 +55,15 @@ } .c-pelle-useless--unavailable { - animation: none; color: black; filter: grayscale(90%); + animation: none; } .c-pelle-useless--unavailable:hover { - cursor: default; background-color: var(--color-bad); filter: grayscale(0%); + cursor: default; } .c-pelle-useless--bought { @@ -72,15 +72,15 @@ } .o-time-study { - border: 0.1rem solid; - background-color: black; - font-weight: bold; - font-family: Typewriter, serif; width: 18rem; height: 10rem; + font-family: Typewriter, serif; + font-size: 1rem; + font-weight: bold; + background-color: black; + border: 0.1rem solid; + border-radius: var(--var-border-radius, 0.4rem); transition-duration: 0.2s; - border-radius: 0.4rem; - font-size: 1.0rem; } .o-time-study--available { @@ -89,8 +89,8 @@ } .o-time-study--available:hover { - animation: none; background-color: #00bcd4; + animation: none; } .o-time-study--unavailable { @@ -114,21 +114,17 @@ border: none; } -.s-base--metro .o-time-study { - border-radius: 0; -} - @keyframes a-time-study { - 0% { box-shadow: inset 0 0 0.3rem 0 } - 50% { box-shadow: inset 0 0 2rem 0 } - 100% { box-shadow: inset 0 0 0.3rem 0 } + 0% { box-shadow: inset 0 0 0.3rem 0; } + 50% { box-shadow: inset 0 0 2rem 0; } + 100% { box-shadow: inset 0 0 0.3rem 0; } } .o-time-study--small { width: 12rem; font-size: 0.9rem; - padding-left: 0.5rem; padding-right: 0.5rem; + padding-left: 0.5rem; } .o-time-study-normal--available { @@ -235,7 +231,7 @@ } .o-time-study-dilation--available:hover { - color: #64DD17; + color: #64dd17; background-color: white; animation: a-time-study-dilation 10s infinite; } @@ -252,9 +248,9 @@ } .o-time-study-triad--available { - border-color: black; color: var(--color-v--base); background: black; + border-color: black; animation: a-time-study 1.5s infinite; } @@ -264,11 +260,11 @@ } @keyframes a-time-study-dilation { - 0% { box-shadow: inset 0.5rem 0 2rem } - 25% { box-shadow: inset 0 0.5rem 2rem } - 50% { box-shadow: inset -0.5rem 0 2rem } - 75% { box-shadow: inset 0 -0.5rem 2rem } - 100% { box-shadow: inset 0.5rem 0 2rem } + 0% { box-shadow: inset 0.5rem 0 2rem; } + 25% { box-shadow: inset 0 0.5rem 2rem; } + 50% { box-shadow: inset -0.5rem 0 2rem; } + 75% { box-shadow: inset 0 -0.5rem 2rem; } + 100% { box-shadow: inset 0.5rem 0 2rem; } } .o-time-study-normal--unavailable { @@ -332,44 +328,44 @@ .o-time-study-normal--bought { color: black; - border-color: black; background-color: var(--color-eternity); + border-color: black; } .o-time-study-antimatter-dim--bought { color: black; - border-color: black; background-color: #22aa48; + border-color: black; } .o-time-study-infinity-dim--bought { color: black; - border-color: black; background-color: #b67f33; + border-color: black; } .o-time-study-time-dim--bought { color: black; - border-color: black; background-color: #b241e3; + border-color: black; } .o-time-study-active--bought { color: black; - border-color: black; background-color: #e60000; + border-color: black; } .o-time-study-passive--bought { color: black; - border-color: black; background-color: #5e33b6; + border-color: black; } .o-time-study-idle--bought { color: black; - border-color: black; background-color: #0080ff; + border-color: black; } .o-time-study-dark--bought { @@ -393,16 +389,16 @@ .o-time-study-eternity-challenge--running { color: var(--color-eternity); background-color: black; - animation: a-eternity-challenge-running 6s infinite; border: 0.1rem solid var(--color-eternity); + animation: a-eternity-challenge-running 6s infinite; } @keyframes a-eternity-challenge-running { - 0% { box-shadow: inset 1rem 0 3rem #490066 } - 25% { box-shadow: inset 0 0.5rem 3rem #490066 } - 50% { box-shadow: inset -1rem 0 3rem #490066 } - 75% { box-shadow: inset 0 -0.5rem 3rem #490066 } - 100% { box-shadow: inset 1rem 0 3rem #490066 } + 0% { box-shadow: inset 1rem 0 3rem #490066; } + 25% { box-shadow: inset 0 0.5rem 3rem #490066; } + 50% { box-shadow: inset -1rem 0 3rem #490066; } + 75% { box-shadow: inset 0 -0.5rem 3rem #490066; } + 100% { box-shadow: inset 1rem 0 3rem #490066; } } .o-time-study-dilation--bought { @@ -460,37 +456,40 @@ } .o-time-study--secret { + opacity: 0; transition: opacity 1s; transition-delay: 1s; - opacity: 0; cursor: pointer; } .o-time-study--secret-unlocked { - transition: opacity 1s; opacity: 1; + transition: opacity 1s; transition-delay: 0s; cursor: pointer; } -.o-time-study--secret-enslaved { +.o-time-study--enslaved { opacity: 0.02; - background: #f1aa7f; - transition: opacity 2s 0.5s; - cursor: pointer; + background: var(--color-enslaved--base); + transition: all 1s; animation: a-time-study 0.5s infinite; + cursor: pointer; } -.o-time-study--secret-enslaved-unlocked { +.o-time-study--enslaved-unlocked { + opacity: 1; + background: var(--color-eternity); + animation: a-time-study 2s infinite; cursor: default; } -/*#endregion o-time-study*/ +/* #endregion o-time-study */ .o-time-study-connection { + stroke: #444444; /* This one should be px, because it rem svg behaves weirdly under scale */ stroke-width: 16px; - stroke: #444444; transition: stroke 0.3s; } @@ -567,12 +566,12 @@ } .o-time-study-connection--secret { - transition: opacity 1s; opacity: 0; + transition: opacity 1s; } .o-time-study-connection--secret-unlocked { + opacity: 1; transition: opacity 1s; transition-delay: 1s; - opacity: 1; } diff --git a/public/stylesheets/tooltips.css b/public/stylesheets/tooltips.css index 588fe760b..06ad53586 100644 --- a/public/stylesheets/tooltips.css +++ b/public/stylesheets/tooltips.css @@ -5,30 +5,35 @@ } .general-tooltip .tooltip-inner { - background: rgba(0, 0, 0, 0.9); - color: white; - border-radius: 3px; - width: 160px; + width: 16rem; text-align: center; - padding: 5px 10px 4px; font-family: Typewriter, serif; - font-size: 14px; + font-size: 1.4rem; line-height: 1.2; + color: white; + background: #0d0d0d; + border-radius: var(--var-border-radius, 0.3rem); + padding: 0.5rem 1rem 0.4rem; + -webkit-user-select: none; user-select: none; } +.s-base--dark .general-tooltip .tooltip-inner { + border: 0.1rem solid #dddddd; +} + .tooltip-inner.automator-tooltip { + display: inline-block !important; overflow: auto; width: unset; height: 100%; - display: inline-block !important; text-align: left; } .automator-tooltip-header { white-space: nowrap; - font-weight: bold; min-width: 16rem; + font-weight: bold; padding-bottom: 1rem; } @@ -38,17 +43,21 @@ } .automator-tooltip-content div { - width: 0; flex-grow: 1; + width: 0; } .general-tooltip .tooltip-arrow { width: 0; height: 0; position: absolute; - margin: 5px; - border: solid rgba(0, 0, 0, 0.9); z-index: 1; + border: solid #0d0d0d; + margin: 5px; +} + +.s-base--dark .general-tooltip .tooltip-arrow { + border: solid #dddddd; } .general-tooltip[x-placement^="top"] { @@ -56,12 +65,12 @@ } .general-tooltip[x-placement^="top"] .tooltip-arrow { - border-width: 5px 5px 0 5px !important; - border-left-color: transparent !important; - border-right-color: transparent !important; - border-bottom-color: transparent !important; bottom: -5px; left: calc(50% - 5px); + border-width: 5px 5px 0 !important; + border-right-color: transparent !important; + border-bottom-color: transparent !important; + border-left-color: transparent !important; margin-top: 0; margin-bottom: 0; } @@ -71,12 +80,12 @@ } .general-tooltip[x-placement^="bottom"] .tooltip-arrow { - border-width: 0 5px 5px 5px; - border-left-color: transparent !important; - border-right-color: transparent !important; - border-top-color: transparent !important; top: -5px; left: calc(50% - 5px); + border-width: 0 5px 5px; + border-top-color: transparent !important; + border-right-color: transparent !important; + border-left-color: transparent !important; margin-top: 0; margin-bottom: 0; } @@ -86,14 +95,14 @@ } .general-tooltip[x-placement^="right"] .tooltip-arrow { + top: calc(50% - 5px); + left: -5px; border-width: 5px 5px 5px 0; - border-left-color: transparent !important; border-top-color: transparent !important; border-bottom-color: transparent !important; - left: -5px; - top: calc(50% - 5px); - margin-left: 0; + border-left-color: transparent !important; margin-right: 0; + margin-left: 0; } .general-tooltip[x-placement^="left"] { @@ -101,40 +110,40 @@ } .general-tooltip[x-placement^="left"] .tooltip-arrow { + top: calc(50% - 5px); + right: -5px; border-width: 5px 0 5px 5px; border-top-color: transparent !important; border-right-color: transparent !important; border-bottom-color: transparent !important; - right: -5px; - top: calc(50% - 5px); - margin-left: 0; margin-right: 0; + margin-left: 0; } .general-tooltip.popover .popover-inner { - background: #f9f9f9; color: black; + background: #f9f9f9; + border-radius: var(--var-border-radius, 5px); + box-shadow: 0 5px 30px rgba(0, 0, 0, 10%); padding: 24px; - border-radius: 5px; - box-shadow: 0 5px 30px rgba(0, 0, 0, 0.1); } .general-tooltip.popover .popover-arrow { border-color: #f9f9f9; } -.general-tooltip[aria-hidden='true'] { +.general-tooltip[aria-hidden="true"] { visibility: hidden; opacity: 0; transition: opacity 0.3s, visibility 0.3s; } -.general-tooltip[aria-hidden='false'] { +.general-tooltip[aria-hidden="false"] { visibility: visible; opacity: 1; transition: opacity 0.3s; } .c-block-automator-error-container .tooltip-inner { - width: auto; -} \ No newline at end of file + width: auto; +} diff --git a/public/stylesheets/vue-sfc-classes.css b/public/stylesheets/vue-sfc-classes.css index 156e873f8..593eab77c 100644 --- a/public/stylesheets/vue-sfc-classes.css +++ b/public/stylesheets/vue-sfc-classes.css @@ -40,11 +40,12 @@ .c-news-line { display: inline-block; + white-space: nowrap; + text-align: left; font-family: Typewriter, serif; font-size: 1.5rem; - white-space: nowrap; font-weight: bold; - text-align: left; + -webkit-user-select: none; user-select: none; } @@ -54,9 +55,9 @@ .new-ui .c-news-ticker { width: 100%; + height: 3.9rem; border-bottom: 0.1rem solid var(--color-accent); padding: 0.8rem 0; - height: 3.9rem; } .new-ui .c-news-line { @@ -64,10 +65,10 @@ } .old-ui .c-news-ticker { - border: 0.2rem solid black; - border-radius: 0.4rem; - padding: 0.2rem 0; height: 3rem; + border: var(--var-border-width, 0.2rem) solid black; + border-radius: var(--var-border-radius, 0.4rem); + padding: 0.2rem 0; } .old-ui .s-base--metro .c-news-ticker, @@ -77,8 +78,8 @@ } .old-ui .t-dark .c-news-ticker { - border-color: #546e7a; background-color: #455a64; + border-color: #546e7a; } .old-ui .t-dark-metro .c-news-ticker { @@ -86,7 +87,7 @@ } .old-ui .t-s1 .c-news-ticker { - background-color: #dbd242 + background-color: #dbd242; } .old-ui .t-s6 .c-news-ticker, diff --git a/src/commit-watcher.js b/src/commit-watcher.js index 126d04159..808eeb9e0 100644 --- a/src/commit-watcher.js +++ b/src/commit-watcher.js @@ -22,8 +22,11 @@ export function watchLatestCommit() { Modal.message.show( "Refresh the page (game will be saved), we've got new stuff: " + `"${json.message}" by ${json.author}`, - updateRefresh, - true + { + callback: updateRefresh, + closeButton: true + }, + 3 ); }); } diff --git a/src/components/BackgroundAnimations.vue b/src/components/BackgroundAnimations.vue index f3bd72fc3..4a02cfdec 100644 --- a/src/components/BackgroundAnimations.vue +++ b/src/components/BackgroundAnimations.vue @@ -1,26 +1,39 @@ \ No newline at end of file +
+ + +
+ + + \ No newline at end of file diff --git a/src/components/BlobBackground.vue b/src/components/BlobBackground.vue index e87d07514..ead7f4563 100644 --- a/src/components/BlobBackground.vue +++ b/src/components/BlobBackground.vue @@ -45,14 +45,16 @@ export default { \ No newline at end of file + diff --git a/src/components/BlobSnowflake.vue b/src/components/BlobSnowflake.vue index f9af5934f..9ecb6e72f 100644 --- a/src/components/BlobSnowflake.vue +++ b/src/components/BlobSnowflake.vue @@ -97,14 +97,16 @@ export default { \ No newline at end of file + diff --git a/src/components/BlobSnowflakes.vue b/src/components/BlobSnowflakes.vue index 5196ce936..cb1a04120 100644 --- a/src/components/BlobSnowflakes.vue +++ b/src/components/BlobSnowflakes.vue @@ -1,6 +1,6 @@ \ No newline at end of file + + + + diff --git a/src/components/ChallengeTabHeader.vue b/src/components/ChallengeTabHeader.vue index 4e16947d4..87c73dadc 100644 --- a/src/components/ChallengeTabHeader.vue +++ b/src/components/ChallengeTabHeader.vue @@ -10,6 +10,7 @@ export default { }, data() { return { + retryChallenge: false, isInChallenge: false, isShowAllVisible: false, isAutoECVisible: false, @@ -18,6 +19,9 @@ export default { }; }, watch: { + retryChallenge(newValue) { + player.options.retryChallenge = newValue; + }, autoEC(newValue) { player.reality.autoEC = newValue; }, @@ -27,10 +31,11 @@ export default { }, methods: { update() { + this.retryChallenge = player.options.retryChallenge; this.showAllChallenges = player.options.showAllChallenges; this.isInChallenge = Player.isInAnyChallenge; this.isShowAllVisible = PlayerProgress.eternityUnlocked(); - this.isAutoECVisible = Perk.autocompleteEC1.isBought && !Pelle.isDoomed; + this.isAutoECVisible = Perk.autocompleteEC1.canBeApplied; this.autoEC = player.reality.autoEC; }, restartChallenge() { @@ -56,6 +61,11 @@ export default { v-if="isShowAllVisible || isAutoECVisible || isInChallenge" class="c-subtab-option-container" > + +export default { + name: "CreditsDisplay", + props: { + isModal: { + type: Boolean, + required: false, + default: false + } + }, + computed: { + people() { return GameDatabase.credits.people; }, + roles() { return GameDatabase.credits.roles; }, + }, + methods: { + relevantPeople(role) { + return this.people + .filter(x => (typeof x.roles === "number" ? x.roles === role : x.roles.includes(role))) + .sort((a, b) => a.name.localeCompare(b.name)); + }, + } +}; + + + + + \ No newline at end of file diff --git a/src/components/CustomizeableTooltip.vue b/src/components/CustomizeableTooltip.vue index 0e3959a76..a4e32d5a8 100644 --- a/src/components/CustomizeableTooltip.vue +++ b/src/components/CustomizeableTooltip.vue @@ -36,7 +36,12 @@ export default { default: "" }, contentClass: { - type: String, + type: [Object, String], + required: false, + default: "" + }, + tooltipClass: { + type: [Object, String], required: false, default: "" }, @@ -53,7 +58,8 @@ export default { data() { return { hovering: false, - mainContent: null + mainContent: null, + isDarkTheme: false }; }, computed: { @@ -88,9 +94,39 @@ export default { }, showTooltip() { return this.show || this.hovering; + }, + // Manual light-dark differentiation instead of just slapping on a .s-base--dark .c-tooltip is needed + // to minimise specificity to make the custom class specify more styles + tooltipContentLightDarkClass() { + return this.isDarkTheme ? "c-tooltip-content--dark" : ""; + }, + tooltipArrowLightDarkClass() { + return this.isDarkTheme ? "c-tooltip-arrow--dark" : ""; + }, + tooltipInternalClass() { + return { + "c-tooltip-show": this.showTooltip, + [this.tooltipType]: true + }; + }, + tooltipContentClass() { + return [ + this.tooltipInternalClass, + this.tooltipClass, + this.tooltipContentLightDarkClass + ]; + }, + tooltipArrowClass() { + return [ + this.tooltipInternalClass, + this.tooltipArrowLightDarkClass + ]; } }, methods: { + update() { + this.isDarkTheme = Theme.current().isDark(); + }, showNegativeSign(axis) { if (axis === "X") { return this.left ? "-" : ""; @@ -114,14 +150,14 @@ export default {
@@ -137,86 +173,5 @@ export default { position: absolute; } -.c-tooltip-content, -.c-tooltip-arrow { - visibility: hidden; - opacity: 0; - pointer-events: none; - transition: 0.4s linear; - transition-property: opacity, visibility; -} - -.c-tooltip-content { - position: absolute; - padding: 0.7rem; - width: 16rem; - border-radius: 0.3rem; - background-color: hsla(0, 0%, 5%, 0.9); - color: #fff; - content: attr(ach-tooltip); - text-align: center; - font-size: 1.4rem; - line-height: 1.2; - z-index: 4; -} - -.t-dark-metro .c-tooltip-content { - border-radius: 0; -} - -.c-tooltip-arrow { - position: absolute; - transform: translate(-50%, -100%); - width: 0; - border-top: 0.55rem solid transparent; - border-right: 0.55rem solid transparent; - border-bottom: 0.55rem solid transparent; - border-left: 0.55rem solid transparent; - content: " "; - font-size: 0; - line-height: 0; - transition-duration: 0.4s; - z-index: 4; -} - -.c-tooltip--top.c-tooltip-content { - margin-top: -0.5rem; -} - -.c-tooltip--top.c-tooltip-arrow { - border-top: 0.55rem solid hsla(0, 0%, 5%, 0.9); - border-bottom: 0; -} - -.c-tooltip--bottom.c-tooltip-content { - margin-bottom: -0.5rem; -} - -.c-tooltip--bottom.c-tooltip-arrow { - border-bottom: 0.55rem solid hsla(0, 0%, 5%, 0.9); - border-top: 0; -} - -.c-tooltip--right.c-tooltip-content { - margin-right: -0.5rem; -} - -.c-tooltip--right.c-tooltip-arrow { - border-right: 0.55rem solid hsla(0, 0%, 5%, 0.9); - border-left: 0; -} - -.c-tooltip--left.c-tooltip-content { - margin-left: -0.5rem; -} - -.c-tooltip--left.c-tooltip-arrow { - border-left: 0.55rem solid hsla(0, 0%, 5%, 0.9); - border-right: 0; -} - -.c-tooltip-show { - visibility: visible; - opacity: 1; -} - \ No newline at end of file +/* c-tooltip-content styles in styles.css to make way for custom class colour styling */ + diff --git a/src/components/ExpandingControlBox.vue b/src/components/ExpandingControlBox.vue index e788f159e..66b5599fa 100644 --- a/src/components/ExpandingControlBox.vue +++ b/src/components/ExpandingControlBox.vue @@ -24,6 +24,16 @@ export default { required: false, default: undefined, }, + buttonClass: { + type: String, + required: false, + default: "l-expanding-control-box__button", + }, + autoClose: { + type: Boolean, + required: false, + default: false, + } }, data() { return { @@ -31,6 +41,8 @@ export default { openRequest: false, closedHeight: "1em", openHeight: "1em", + hasMouse: false, + closeTime: 0, }; }, computed: { @@ -63,6 +75,12 @@ export default { }; classes[this.containerClass] = true; return classes; + }, + indicatorArrowClassObject() { + return { + "c-indicator-arrow": true, + "c-indicator-arrow--flipped": this.state === this.states.OPENING || this.state === this.states.OPEN, + }; } }, watch: { @@ -93,7 +111,7 @@ export default { }, created() { this.state = this.states.CLOSED; - this.$on("openrequest", () => this.openRequest = true); + this.on$("openrequest", () => this.openRequest = true); }, mounted() { // Set the root and container elements to match the height of the button @@ -102,6 +120,10 @@ export default { this.updateBaseWidth(); }, methods: { + update() { + const secSinceMouseOff = this.hasMouse ? 0 : (Date.now() - this.closeTime) / 1000; + if (this.autoClose && this.state === this.states.OPEN && secSinceMouseOff > 1) this.openRequest = false; + }, processRequest(state, request) { if (request && (state === this.states.CLOSED || state === this.states.CLOSE_REQUESTED)) { this.state = this.states.OPEN_REQUESTED; @@ -131,6 +153,17 @@ export default { this.state = this.states.CLOSED; } }, + handleClick() { + this.openRequest = !this.openRequest; + this.hasMouse = this.openRequest; + }, + mouseOn() { + this.hasMouse = true; + }, + mouseOff() { + this.hasMouse = false; + this.closeTime = Date.now(); + } } }; @@ -153,19 +186,24 @@ export default { :class="containerClassObject" :style="containerStyle" @transitionend="transitionEnd" + @mouseenter="mouseOn" + @mouseleave="mouseOff" >
- {{ label }} ▼ + {{ label }} + + ▼ +
@@ -187,27 +225,38 @@ export default { } .l-expanding-control-box__container { - position: absolute; display: block; - height: auto; overflow: hidden; width: 100%; + height: auto; + position: absolute; left: 50%; transform: translateX(-50%); - -webkit-transform: translateX(-50%); } .l-expanding-control-box__container--transition { transition: max-height 0.5s; - -webkit-transition: max-height 0.5s; } .l-expanding-control-box__button { - cursor: pointer; - display: block; - width: 100%; + display: flex; white-space: nowrap; - border: none !important; + width: 100%; height: 2.5rem; + position: relative; + top: -0.5rem; + justify-content: center; + align-items: center; + border: none !important; + cursor: pointer; +} + +.c-indicator-arrow--flipped { + transform: rotate(-180deg); +} + +.c-indicator-arrow { + margin-left: 0.6rem; + transition: transform 0.25s ease-out; } diff --git a/src/components/GameSpeedDisplay.vue b/src/components/GameSpeedDisplay.vue index b9f258079..ab51e5cd5 100644 --- a/src/components/GameSpeedDisplay.vue +++ b/src/components/GameSpeedDisplay.vue @@ -1,10 +1,13 @@ + + diff --git a/src/components/GameUIComponent.vue b/src/components/GameUIComponent.vue index 03fccb61e..79f4d44a7 100644 --- a/src/components/GameUIComponent.vue +++ b/src/components/GameUIComponent.vue @@ -1,19 +1,9 @@ @@ -67,43 +47,27 @@ export default { class="c-game-ui" > - + - - - - - -
- - - - - - - + +
\ No newline at end of file + diff --git a/src/components/GameUiComponentFixed.vue b/src/components/GameUiComponentFixed.vue new file mode 100644 index 000000000..b40535610 --- /dev/null +++ b/src/components/GameUiComponentFixed.vue @@ -0,0 +1,114 @@ + + + + + + diff --git a/src/components/GenericDimensionRowText.vue b/src/components/GenericDimensionRowText.vue new file mode 100644 index 000000000..f97e42174 --- /dev/null +++ b/src/components/GenericDimensionRowText.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/src/components/GlyphComponent.vue b/src/components/GlyphComponent.vue index 4f773e47b..793eca2b8 100644 --- a/src/components/GlyphComponent.vue +++ b/src/components/GlyphComponent.vue @@ -1,4 +1,6 @@ @@ -439,30 +539,36 @@ export default { :style="glyphEffectIcon(x)" /> - +
New!
+
+ {{ displayedInfo }} +
-// If you change this, try to keep the syntaxes and tenses the same to avoid solecisms const GLYPH_NAMES = { - companion: { name: "Huggable" }, - reality: { name: "Real" }, - music: { major: "Melodic", middling: "Chordal", minor: "Tuned" }, - effarig: { both: "Meta", glyph: "Stable", rm: "Mechanical", none: "Fragmented" }, - cursed: { major: "Cursed", middling: "Hexed", minor: "Jinxed" }, - power: { major: "Power", middling: "Mastered", minor: "Potential" }, - infinity: { major: "Infinity", middling: "Boundless", minor: "Immense" }, - replication: { major: "Replication", middling: "Simulated", minor: "Replicated" }, - time: { major: "Time", middling: "Chronal", minor: "Temporal" }, - dilation: { major: "Dilation", middling: "Attenuated", minor: "Diluted" }, + companion: { + adjective: "Huggable", + noun: "Companion" + }, + reality: { + adjective: "Real", + noun: "Reality" + }, + music: { + adjective: { high: "Melodic", mid: "Chordal", low: "Tuned" } + // This doesn't need noun entries because music glyphs also apply their actual types + }, + effarig: { + adjective: { both: "Meta", glyph: "Stable", rm: "Mechanical", none: "Fragmented" }, + noun: { both: "Effarig", glyph: "Stability", rm: "Mechanism", none: "Fragmentation" } + }, + cursed: { + adjective: { high: "Cursed", mid: "Hexed", low: "Jinxed" }, + noun: "Curse" + }, + power: { + adjective: { high: "Powerful", mid: "Mastered", low: "Potential" }, + noun: "Power" + }, + infinity: { + adjective: { high: "Infinite", mid: "Boundless", low: "Immense" }, + noun: "Infinity" + }, + replication: { + adjective: { high: "Replicated", mid: "Simulated", low: "Duplicated" }, + noun: "Replication" + }, + time: { + adjective: { high: "Temporal", mid: "Chronal", low: "Transient" }, + noun: "Time" + }, + dilation: { + adjective: { high: "Dilated", mid: "Attenuated", low: "Diluted" }, + noun: "Dilation" + }, }; export default { @@ -29,153 +58,129 @@ export default { data() { return { isColored: true, - defaultOrder: ["power", "infinity", "replication", "time", "dilation"], - multipleGlyphList: [ - { type: "power", perc: 0 }, - { type: "infinity", perc: 0 }, - { type: "replication", perc: 0 }, - { type: "time", perc: 0 }, - { type: "dilation", perc: 0 } + // Adjectives are added in descending order of adjOrder (basic glyphs are handled together) + glyphTypeList: [ + { type: "power", perc: 0, adjOrder: 1 }, + { type: "infinity", perc: 0, adjOrder: 1 }, + { type: "replication", perc: 0, adjOrder: 1 }, + { type: "time", perc: 0, adjOrder: 1 }, + { type: "dilation", perc: 0, adjOrder: 1 }, + { type: "effarig", perc: 0, adjOrder: 2 }, + { type: "music", perc: 0, adjOrder: 3 }, + { type: "reality", perc: 0, adjOrder: 4 }, + { type: "companion", perc: 0, adjOrder: 5 }, + { type: "cursed", perc: 0, adjOrder: 6 }, ], - activeSlotCount: 0 + sortedGlyphs: [], + slotCount: 0 }; }, computed: { + isDoomed: () => Pelle.isDoomed, setName() { this.sortGlyphList(); - let nameString = ""; + if (this.sortedGlyphs.length === 0) return "Void"; + if (this.sortedGlyphs.length === 1) return this.singletonName; - // Start with Companion and Reality, add those to the start - if (this.calculateGlyphPercent("companion")) { - nameString += `${GLYPH_NAMES.companion.name} `; - } - if (this.calculateGlyphPercent("reality")) { - nameString += `${GLYPH_NAMES.reality.name} `; + // Figure out the noun part of the name first. If we have basic glyphs, this is generated through examining those + // specifically. Otherwise, we take the lowest-priority special glyph and turn it into its noun form + let adjList, nounPhrase; + if (this.sortedGlyphs.some(t => t.adjOrder === 1)) { + adjList = this.sortedGlyphs.filter(t => t.adjOrder !== 1); + nounPhrase = this.basicTypePhrase; + } else { + adjList = [...this.sortedGlyphs]; + nounPhrase = this.getNoun(adjList.pop()); } - // Now on to Music Glyphs - seperate from the others as they can be both Music and [type] - if (this.musicGlyphPercent() === 100) { - nameString += `${GLYPH_NAMES.music.major} `; - } else if (this.musicGlyphPercent() >= 40) { - nameString += `${GLYPH_NAMES.music.middling} `; - } else if (this.musicGlyphPercent()) { - nameString += `${GLYPH_NAMES.music.minor} `; - } - - // Both, RM, Glyph, Neither each have unique results - const effarigRM = this.glyphSet.some(i => getSingleGlyphEffectFromBitmask("effarigrm", i)); - const effarigGlyph = this.glyphSet.some(i => getSingleGlyphEffectFromBitmask("effarigglyph", i)); - if (this.calculateGlyphPercent("effarig")) { - if (effarigRM && effarigGlyph) nameString += `${GLYPH_NAMES.effarig.both} `; - else if (effarigRM) nameString += `${GLYPH_NAMES.effarig.rm} `; - else if (effarigGlyph) nameString += `${GLYPH_NAMES.effarig.glyph} `; - else nameString += `${GLYPH_NAMES.effarig.none} `; - } - - // Cursed needs a special additional method of generating its names - if (this.calculateGlyphPercent("cursed")) nameString += this.cursedName; - else if (this.multipleGlyphList[0].perc) nameString += this.normalName; - - return nameString; + const adjectives = []; + for (const listEntry of adjList) adjectives.push(this.getAdjective(listEntry)); + return `${adjectives.join(" ")} ${nounPhrase}`; }, - cursedName() { - const cursedPerc = this.calculateGlyphPercent("cursed"); - const main = GLYPH_NAMES.cursed; - const fir = this.multipleGlyphList[0]; - const sec = this.multipleGlyphList[1]; - const thr = this.multipleGlyphList[2]; - if (cursedPerc === 100) { - // All Cursed Glyphs should get something special. - return `Fully ${main.major}`; - } - if (cursedPerc >= 75) { - // Mostly Cursed Glyphs, but should mention the other. - return `${main.major} ${this.getName(fir, "minor")}`; - } - if (cursedPerc >= 60) { - // Have less Cursed Glyphs, so the name's changed, and might have a second now so need a case for that. - return `${main.middling} ${this.getName(fir, "minor")}${this.getName(sec, "minor")}`; - } - if (cursedPerc >= 40) { - // If we have a third normal Glyph, lets call it an Irregularity, otherwise specify the types. - if (fir.perc >= 40) { - return `${main.middling} ${this.getName(sec, "middling")}${this.getName(fir, "major")}`; - } - if (thr.perc) { - return `${main.middling} Irregularity`; - } - } - // If we only have the one Cursed Glyph, lets just add the normal name to it and avoid repetition. - return `${main.minor} ${this.normalName}`; - }, - normalName() { - const fir = this.multipleGlyphList[0]; - const sec = this.multipleGlyphList[1]; - const thr = this.multipleGlyphList[2]; - if (fir.perc === 100) { - // The only Glyph here is the one type so it should get a special name. - return `Full ${this.getName(fir, "major")}`; - } - if (fir.perc >= 75) { - // Two Glyphs, but its mainly the one type. - return `${this.getName(sec, "middling")}${this.getName(fir, "major")}`; - } - if (fir.perc >= 60) { - // Room for 2 other Glyphs off the main, so theres a case for each of them. - return `${thr.perc - ? `${this.getName(sec, "minor")}${this.getName(thr, "minor")}` - : `${this.getName(sec, "middling")}` - }${this.getName(fir, "major")}`; - } - if (fir.perc >= 40) { - if (sec.perc >= 40) { - if (thr.perc) { - // If we have a 3rd type, lets also call it an Irregularity - return `${this.getName(fir, "minor")}${this.getName(sec, "minor")}Irregularity`; + basicTypePhrase() { + const basicGlyphList = this.sortedGlyphs.filter(t => BASIC_GLYPH_TYPES.includes(t.type) && t.perc !== 0); + switch (basicGlyphList.length) { + case 1: + return GLYPH_NAMES[basicGlyphList[0].type].noun; + case 2: + // Call it a mixture if they're equal and apply adjectives of appropriate magnitude + if (basicGlyphList[0].perc === basicGlyphList[1].perc) { + return [this.getAdjective(basicGlyphList[0]), + this.getAdjective(basicGlyphList[1]), + "Mixture" + ].join(" "); } - // Essentially means the same amount for both, so give them the same name. - return `${this.getName(fir, "minor")}${this.getName(sec, "minor")}`; - } - // Second Glyph is less than the first meaning a different name, and if we also have a third, call it Blend. - return `${this.getName(fir, "middling")}${thr.perc ? "Blend" : `${this.getName(sec, "minor")}`}`; + // Otherwise, give it a noun from the largest component + return `${this.getAdjective(basicGlyphList[1])} ${this.getNoun(basicGlyphList[0])}`; + case 3: + // Give it a noun if there's a clear majority + if (basicGlyphList[0].perc > basicGlyphList[1].perc) { + return [this.getAdjective(basicGlyphList[1]), + this.getAdjective(basicGlyphList[2]), + this.getNoun(basicGlyphList[0]), + ].join(" "); + } + // This is relatively rare; we have 1/1/1, which means that we may also already have 3 other adjectives. + // In this case we make an exception and shorten the name instead of providing another 4 words + if (basicGlyphList[0].perc === basicGlyphList[2].perc) return "Mixed Irregularity"; + // The only case left is 2/2/1, where we have plenty of room for words + return [this.getAdjective(basicGlyphList[0]), + this.getAdjective(basicGlyphList[1]), + this.getAdjective(basicGlyphList[2]), + "Irregularity" + ].join(" "); + case 4: + // Don't bother filling the name with excessive adjectives if we have an equal proportion (1/1/1/1), + // otherwise we take the largest component and ignore all the others (2/1/1/1) + if (basicGlyphList[0].perc === basicGlyphList[1].perc) return "Irregular Jumble"; + return `${this.getAdjective(basicGlyphList[0])} Jumble`; + case 5: + // This is in reference to the achievement name, and can only occur with exactly one of every basic glyph. + // Due to music glyphs doubling-up contributions, this may result in a "Melodic Royal Flush" or similar + return "Royal Flush"; + default: + throw new Error("Unexpected glyph set configuration in GlyphSetName"); } - if (thr.perc) { - // This means we have 3 different Glyphs, with only one Glyph of each. - if (!(this.calculateGlyphPercent("reality") || this.calculateGlyphPercent("effarig") || - this.calculateGlyphPercent("cursed"))) { - // If it doesn't have Reality, Effarig, or Cursed Glyphs, call it Irregular Jumble, otherwise call it Jumble. - return "Irregular Jumble"; - } - return "Jumble"; + }, + // Check for single-type sets and give them a special name based on how much of the full equipped slots they take up + singletonName() { + if (this.sortedGlyphs[0].type === "effarig") return GLYPH_NAMES.effarig.noun[this.getEffarigProp()]; + const singleGlyphTypes = ["reality", "companion"]; + for (const key of singleGlyphTypes) { + if (this.sortedGlyphs[0].type === key) return GLYPH_NAMES[key].noun; } - // This means we don't have 3 different Glyphs, but dont have 2 of one type. - return `${this.getName(fir, "minor")}${this.getName(sec, "minor")}`; + + // We want a bit of additional flavor for partially-filled sets + const word = GLYPH_NAMES[this.sortedGlyphs[0].type].noun; + const perc = this.sortedGlyphs[0].perc; + if (this.isDoomed) return `Doomed ${word}`; + if (perc === 100) return `Full ${word}`; + if (perc >= 75) return `Strengthened ${word}`; + if (perc >= 40) return `Partial ${word}`; + return `Weak ${word}`; }, mainGlyphName() { // This returns the type of Glyph that we want for color determinations. - // The priority is Cursed > Companion > Reality > 60% or more of normal Glyphs > Effarig > any normal Glyph. + // The priority is Empty > Cursed > Companion > Reality > 50% or more normal Glyphs > Effarig > any normal Glyph + if (this.sortedGlyphs.length === 0) return { id: "none", color: "#888888" }; if (this.calculateGlyphPercent("cursed")) return GlyphTypes.cursed; if (this.calculateGlyphPercent("companion")) return GlyphTypes.companion; if (this.calculateGlyphPercent("reality")) return GlyphTypes.reality; - if (this.multipleGlyphList[0].perc >= 60) return GlyphTypes[this.multipleGlyphList[0].type]; + if (this.calculateGlyphPercent("music") >= 50) return { id: "music", color: "#FF80AB" }; + const primaryType = this.sortedGlyphs.filter(t => t.adjOrder === 1)[0]; + if (primaryType?.perc >= 50) return GlyphTypes[primaryType.type]; if (this.calculateGlyphPercent("effarig")) return GlyphTypes.effarig; - return GlyphTypes[this.multipleGlyphList[0].type]; - }, - percentPerGlyph() { - // We need to max glyphset length here because Doomed alters the active glyph slots to 0, which breaks stuff - // in the calculation because we're dividing by 0 - return 100 / Math.max(this.activeSlotCount, this.glyphSet.length); + return GlyphTypes[primaryType.type]; }, textColor() { + // If it's the singular equipped glyph in Doomed, we color it crimson // If its cursed, we want its color to be #5151EC, because by default its black, which can be unreadable. - // If we have greater than or equal to 60% of our Glyphs as Music Glyphs, give us the Music Glyph color. - // If we have 3 types of Glyphs, and none of them have more than 25% total, lets get a copper color. + // If we have 3 types of Glyphs, and none of them have more than 30% total, lets get a copper color. // And if we have none of the above (which is most common), lets get the color of the main Glyph. + if (this.isDoomed && this.glyphSet.length === 1) return "var(--color-pelle--base)"; if (this.mainGlyphName.id === "cursed") return "#5151EC"; - if (this.musicGlyphPercent() >= 60) return "#FF80AB"; - if (this.multipleGlyphList[1].perc && this.multipleGlyphList[2].perc && this.multipleGlyphList[0].perc <= 25) { - return "#C46200"; - } + if (this.mainGlyphName.id === "music") return "#FF80AB"; + if (this.sortedGlyphs.length >= 3 && this.sortedGlyphs[0].perc <= 30) return "#C46200"; return this.mainGlyphName.color; }, textStyle() { @@ -201,28 +206,48 @@ export default { methods: { update() { this.isColored = player.options.glyphTextColors; - this.activeSlotCount = Glyphs.activeSlotCount; + // Without max, Doomed may retroactively zero the slot count of older sets in records and mess up their names + // This can retroactively change names on old sets when gaining new slots in reality upgrades, but this is + // probably acceptable since the old names may have become unattainable with the new slot count anyway + this.slotCount = Math.max(Glyphs.activeSlotCount, this.glyphSet.length); + }, + getEffarigProp() { + const effarigRM = this.glyphSet.some(i => getSingleGlyphEffectFromBitmask("effarigrm", i)); + const effarigGlyph = this.glyphSet.some(i => getSingleGlyphEffectFromBitmask("effarigglyph", i)); + if (effarigRM && effarigGlyph) return "both"; + if (effarigRM) return "rm"; + if (effarigGlyph) return "glyph"; + return "none"; }, calculateGlyphPercent(name) { - // Take the amount of a type of glyph in the set, divide by the maximum number of glyphs, then * 100 to get % - return this.glyphSet.filter(i => i.type === name).length * this.percentPerGlyph; - }, - musicGlyphPercent() { + const percentPerGlyph = this.slotCount ? 100 / this.slotCount : 0; // Music Glyphs are tricky to get, have to search .symbol === "key266b" - return this.glyphSet.filter(i => i.symbol === "key266b").length * this.percentPerGlyph; + if (name === "music") return this.glyphSet.filter(i => i.symbol === "key266b").length * percentPerGlyph; + // Take the amount of a type of glyph in the set, divide by the maximum number of glyphs, then * 100 to get % + return this.glyphSet.filter(i => i.type === name).length * percentPerGlyph; }, sortGlyphList() { - // Get the percent for each type, then sort it based on type and then default order, to make it consistent this.$recompute("textColor"); - this.multipleGlyphList.forEach(i => i.perc = this.calculateGlyphPercent(i.type)); - this.multipleGlyphList.sort((a, b) => (a.perc === b.perc - ? this.defaultOrder.indexOf(a.type) - this.defaultOrder.indexOf(b.type) - : b.perc - a.perc)); + this.glyphTypeList.forEach(t => t.perc = this.calculateGlyphPercent(t.type)); + this.sortedGlyphs = this.glyphTypeList.filter(t => t.perc !== 0); + // This composite function is required in order to ensure consistent names with equal percentages, as JS doesn't + // guarantee .sort() operations are stable sorts. Sorts by adjOrder, followed by perc, followed by alphabetical. + const sortFn = t => 100 * t.adjOrder + t.perc + t.type.charCodeAt(0) / 1000; + this.sortedGlyphs.sort((a, b) => sortFn(b) - sortFn(a)); }, - getName(position, type) { - // If the position.perc is 0, return an empty string, and if it does, return a string from GLYPH_NAMES - if (!position.perc) return ``; - return `${GLYPH_NAMES[position.type][type]} `; + getAdjective(listEntry) { + if (listEntry.type === "effarig") return GLYPH_NAMES.effarig.adjective[this.getEffarigProp()]; + const adjFn = val => { + if (val >= 60) return "high"; + if (val >= 40) return "mid"; + return "low"; + }; + const adj = GLYPH_NAMES[listEntry.type].adjective; + return typeof adj === "string" ? adj : adj[adjFn(listEntry.perc)]; + }, + getNoun(listEntry) { + if (listEntry.type === "effarig") return GLYPH_NAMES.effarig.noun[this.getEffarigProp()]; + return GLYPH_NAMES[listEntry.type].noun; }, } }; diff --git a/src/components/GlyphSetPreview.vue b/src/components/GlyphSetPreview.vue index 86b9babd6..d55cd6489 100644 --- a/src/components/GlyphSetPreview.vue +++ b/src/components/GlyphSetPreview.vue @@ -57,6 +57,11 @@ export default { type: String, required: false, default: "(No Glyphs equipped)" + }, + sort: { + type: Boolean, + required: false, + default: true } }, data() { @@ -64,12 +69,28 @@ export default { realityGlyphBoost: 0, }; }, + computed: { + orderedGlyphs() { + if (!this.sort) return this.glyphs; + const standardOrder = ["reality", "effarig", "power", "infinity", "replication", "time", "dilation", + "cursed", "companion"]; + const order = Glyphs.copyForRecords(this.glyphs); + // Technically doesn't stable sort between glyphs of the same type, probably fine though + order.sort((a, b) => standardOrder.indexOf(a.type) - standardOrder.indexOf(b.type)); + return order; + }, + }, + watch: { + glyphs() { + this.$recompute("orderedGlyphs"); + } + }, methods: { update() { // There should only be one reality glyph; this picks one pseudo-randomly if multiple are cheated/glitched in const realityGlyph = this.glyphs.filter(g => g.type === "reality")[0]; this.realityGlyphBoost = realityGlyph - ? GameDatabase.reality.glyphEffects.realityglyphlevel.effect(realityGlyph.level) + ? GlyphEffects.realityglyphlevel.effect(realityGlyph.level) : 0; }, showModal() { @@ -78,8 +99,6 @@ export default { name: this.text, glyphSet: this.glyphs, closeOn: GAME_EVENT.GLYPH_SET_SAVE_CHANGE, - isGlyphSelection: false, - showSetName: true, displaySacrifice: this.showSacrifice, }); } @@ -95,7 +114,7 @@ export default { + {{ noneText }}
diff --git a/src/components/GlyphTooltip.vue b/src/components/GlyphTooltip.vue index e7690fc6e..de8053082 100644 --- a/src/components/GlyphTooltip.vue +++ b/src/components/GlyphTooltip.vue @@ -66,6 +66,12 @@ export default { required: true } }, + data() { + return { + showChaosText: false, + chaosDescription: "" + }; + }, computed: { onTouchDevice() { return GameUI.touchDevice; @@ -76,22 +82,28 @@ export default { sortedEffects() { return getGlyphEffectValuesFromBitmask(this.effects, this.effectiveLevel, this.strength, this.type) .filter(effect => - GameDatabase.reality.glyphEffects[effect.id].isGenerated === generatedTypes.includes(this.type)); + GlyphEffects[effect.id].isGenerated === generatedTypes.includes(this.type)); }, rarityInfo() { return getRarity(this.strength); }, + baseColor() { + if (this.type === "cursed") return Theme.current().isDark() || player.options.forceDarkGlyphs ? "white" : "black"; + return Theme.current().isDark() || player.options.forceDarkGlyphs ? "black" : "white"; + }, + textColor() { + if (this.type === "cursed") return Theme.current().isDark() || player.options.forceDarkGlyphs ? "black" : "white"; + return Theme.current().isDark() || player.options.forceDarkGlyphs ? "white" : "black"; + }, + mainBorderColor() { + if (this.type === "cursed") return this.textColor; + if (this.type === "companion") return GlyphTypes[this.type].color; + return getColor(this.strength); + }, descriptionStyle() { - let color = this.rarityInfo.color; - if (this.type === "cursed") color = "black"; - if (this.type === "companion") color = GlyphTypes[this.type].color; + const color = this.mainBorderColor; return { color, - "text-shadow": this.type === "cursed" - ? undefined - : `-0.1rem 0.1rem 0.1rem black, 0.1rem 0.1rem 0.1rem black, - -0.1rem -0.1rem 0.1rem black, 0.1rem -0.1rem 0.1rem black, - 0 0 0.3rem ${color}`, animation: this.type === "reality" ? "a-reality-glyph-name-cycle 10s infinite" : undefined }; }, @@ -129,7 +141,7 @@ export default { // eslint-disable-next-line no-nested-ternary const color = this.isLevelCapped ? "#ff4444" - : (this.isLevelBoosted ? "#44FF44" : ""); + : (this.isLevelBoosted ? "#44FF44" : undefined); return `Level: ${arrow}${formatInt(this.effectiveLevel)}${arrow} `; @@ -144,28 +156,29 @@ export default { glyphTooltipStyle() { // With computer mice, it's nice to just totally disable mouse events on the tooltip, // which reduces the chances for stupidity + const borderColor = this.type === "cursed" ? this.textColor : GlyphTypes[this.type].color; return { "pointer-events": this.onTouchDevice ? undefined : "none", - "border-color": GlyphTypes[this.type].color, - "box-shadow": `0 0 0.5rem ${GlyphTypes[this.type].color}, 0 0 0.5rem ${GlyphTypes[this.type].color} inset`, + "border-color": borderColor, + "box-shadow": `0 0 0.5rem ${borderColor}, 0 0 0.5rem ${borderColor} inset`, animation: this.type === "reality" ? "a-reality-glyph-tooltip-cycle 10s infinite" : undefined, - color: this.type === "cursed" ? "black" : undefined, - background: this.type === "cursed" ? "white" : undefined + color: this.textColor, + background: this.baseColor }; }, glyphHeaderStyle() { const isCursed = this.type === "cursed"; const isReality = this.type === "reality"; - let color = this.rarityInfo.color; - if (isCursed) color = "black"; + let color = Theme.current().isDark() ? this.rarityInfo.darkColor : this.rarityInfo.lightColor; + if (isCursed) color = this.textColor; if (this.type === "companion") color = GlyphTypes[this.type].color; return { "border-color": color, "box-shadow": `0 0 0.5rem 0.1rem ${color}, 0 0 0.8rem ${color} inset`, animation: isReality ? "a-reality-glyph-tooltip-header-cycle 10s infinite" : undefined, - color: isCursed ? "black" : undefined, - background: isCursed ? "white" : undefined + color: this.textColor, + background: this.baseColor }; } }, @@ -189,6 +202,12 @@ export default { } }, methods: { + update() { + this.showChaosText = Pelle.specialGlyphEffect.isUnlocked; + if (this.showChaosText) { + this.chaosDescription = Pelle.getSpecialGlyphEffectDescription(this.type); + } + }, touchStart() { // We _don't_ preventDefault here because we want the event to turn into a local // dragstart that we can intercept @@ -206,11 +225,14 @@ export default { removeGlyph() { GlyphSacrificeHandler.removeGlyph(Glyphs.findById(this.id), false); }, + getFontColor() { + return Theme.current().isDark() ? "#cccccc" : "black"; + }, sacrificeText() { if (this.type === "companion" || this.type === "cursed") return ""; const powerText = `${format(this.sacrificeReward, 2, 2)}`; const isCurrentAction = this.currentAction === "sacrifice"; - return ` + return ` Sacrifice: ${powerText} `; }, @@ -222,7 +244,7 @@ export default { refinementText += ` (Actual value due to cap: ${format(this.refineReward, 2, 2)} ${GLYPH_SYMBOLS[this.type]})`; } const isCurrentAction = this.currentAction === "refine"; - return ` + return ` Refine: ${refinementText} `; }, @@ -274,6 +296,12 @@ export default { :effect="e.id" :value="e.value" /> +
+ {{ chaosDescription }} +
diff --git a/src/components/GlyphTooltipEffect.vue b/src/components/GlyphTooltipEffect.vue index f8dd91909..470e8e973 100644 --- a/src/components/GlyphTooltipEffect.vue +++ b/src/components/GlyphTooltipEffect.vue @@ -13,7 +13,7 @@ export default { }, computed: { effectConfig() { - return GameDatabase.reality.glyphEffects[this.effect]; + return GlyphEffects[this.effect]; }, boostColor() { return (this.effectConfig.alterationType !== undefined && @@ -27,18 +27,16 @@ export default { : undefined; }, effectStringTemplate() { - return typeof this.effectConfig.singleDesc === "function" - ? this.effectConfig.singleDesc() - : this.effectConfig.singleDesc; + return this.effectConfig.singleDesc; }, primaryEffectText() { const value = this.effectConfig.formatSingleEffect(this.value); - return this.boostColor ? `⯅${value}⯅` : value; + return this.boostColor ? `⯅${value}` : value; }, secondaryEffectText() { const value = this.effectConfig.formatSingleSecondaryEffect( this.effectConfig.conversion(this.value)); - return this.boostColor ? `⯅${value}⯅` : value; + return this.boostColor ? `⯅${value}` : value; }, textSplits() { const firstSplit = this.effectStringTemplate.split("{value}"); @@ -53,7 +51,7 @@ export default { return this.textSplits[2] !== undefined; }, isPelleDisabled() { - return Pelle.isDoomed && !Pelle.enabledGlyphEffects.includes(this.effect); + return this.effectConfig.isDisabledByDoomed; }, convertedParts() { const parts = []; @@ -68,13 +66,21 @@ export default { color: "#76EE76", }; }, + textShadowColor() { + return Theme.current().isDark() || player.options.forceDarkGlyphs ? "white" : "black"; + }, }, methods: { convertToHTML(string) { return string .replace("\n", "
") .replace("]", "
") - .replace("[", ``); + .replace( + "[", `` + ); } } }; @@ -83,24 +89,19 @@ export default { diff --git a/src/components/PlusMinusButton.vue b/src/components/PlusMinusButton.vue index 2221ca6d5..9c3260468 100644 --- a/src/components/PlusMinusButton.vue +++ b/src/components/PlusMinusButton.vue @@ -26,3 +26,27 @@ export default { + diff --git a/src/components/RealityCurrencyHeader.vue b/src/components/RealityCurrencyHeader.vue new file mode 100644 index 000000000..2432aa8b7 --- /dev/null +++ b/src/components/RealityCurrencyHeader.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/src/components/RealityMachinesHeader.vue b/src/components/RealityMachinesHeader.vue deleted file mode 100644 index cf98f99e3..000000000 --- a/src/components/RealityMachinesHeader.vue +++ /dev/null @@ -1,38 +0,0 @@ - - - - - diff --git a/src/components/SaveTimer.vue b/src/components/SaveTimer.vue index a603a8d8a..6d074db80 100644 --- a/src/components/SaveTimer.vue +++ b/src/components/SaveTimer.vue @@ -38,23 +38,20 @@ export default { \ No newline at end of file + diff --git a/src/components/SliderComponent.vue b/src/components/SliderComponent.vue index e7b8cd9d3..38fd088ef 100644 --- a/src/components/SliderComponent.vue +++ b/src/components/SliderComponent.vue @@ -449,6 +449,11 @@ export default { position: "relative" } }, + draggableStyle() { + return { + cursor: this.boolDisabled ? "default" : "pointer" + }; + }, dotStyles() { let ret = { width: this.dotWidthVal, @@ -831,6 +836,7 @@ export default { } }, increment(dir) { + if (this.boolDisabled) return; const newVal = this.getValue() + dir * this.interval; if (newVal > this.max || newVal < this.min) return; this.setValue(newVal); @@ -1030,7 +1036,11 @@ export default { :class="['l-ad-slider__wrap', stateClass]" :style="[wrapStyles, boolDisabled ? disabledStyle : null]" @click="wrapClick"> - @@ -95,6 +66,7 @@ export default { .l-information-modal { display: flex; flex-direction: column; + /* stylelint-disable-next-line unit-allowed-list */ width: calc(100vw - 50vh); justify-content: space-around; } @@ -104,10 +76,10 @@ export default { } .c-info-body { - margin: 1rem; - padding: 0.5rem; text-align: left; font-size: 2rem; + margin: 1rem; + padding: 0.5rem; } .c-socials { @@ -116,24 +88,7 @@ export default { .l-socials { display: flex; - align-items: center; justify-content: space-evenly; + align-items: center; } - -.c-socials--icon { - color: var(--color-text); -} - -.c-socials--icon__wrapper { - transition: all .2s ease-in-out; -} - -.c-socials--icon__wrapper:hover { - transform: scale(1.1); -} - -[ach-tooltip]:before { - width: 20rem; - font-size: 1.5rem; -} - \ No newline at end of file + diff --git a/src/components/modals/InformationModalButton.vue b/src/components/modals/InformationModalButton.vue new file mode 100644 index 000000000..f5af5c87b --- /dev/null +++ b/src/components/modals/InformationModalButton.vue @@ -0,0 +1,73 @@ + + + + + \ No newline at end of file diff --git a/src/components/modals/LoadGameEntry.vue b/src/components/modals/LoadGameEntry.vue index effe44e99..bbef9a554 100644 --- a/src/components/modals/LoadGameEntry.vue +++ b/src/components/modals/LoadGameEntry.vue @@ -15,7 +15,8 @@ export default { data() { const save = GameStorage.saves[this.saveId]; return { - antimatter: new Decimal(save ? save.antimatter || save.money : 10) + antimatter: new Decimal(save ? save.antimatter || save.money : 10), + fileName: save ? save.options.saveFileName : "" }; }, computed: { @@ -42,6 +43,7 @@ export default { + + diff --git a/src/components/modals/MessageModal.vue b/src/components/modals/MessageModal.vue index c933386ab..ec6786e92 100644 --- a/src/components/modals/MessageModal.vue +++ b/src/components/modals/MessageModal.vue @@ -1,6 +1,6 @@ + + diff --git a/src/components/modals/PelleEffectsModal.vue b/src/components/modals/PelleEffectsModal.vue index ac311c55a..55f938789 100644 --- a/src/components/modals/PelleEffectsModal.vue +++ b/src/components/modals/PelleEffectsModal.vue @@ -1,6 +1,6 @@ diff --git a/src/components/modals/options/ConfirmationOptionsModal.vue b/src/components/modals/options/ConfirmationOptionsModal.vue index 0b969d1e9..439d30c22 100644 --- a/src/components/modals/options/ConfirmationOptionsModal.vue +++ b/src/components/modals/options/ConfirmationOptionsModal.vue @@ -1,6 +1,6 @@ + + + + diff --git a/src/components/modals/options/InfoDisplayOptionsModal.vue b/src/components/modals/options/InfoDisplayOptionsModal.vue index 1a1bd39c5..5c4792dc6 100644 --- a/src/components/modals/options/InfoDisplayOptionsModal.vue +++ b/src/components/modals/options/InfoDisplayOptionsModal.vue @@ -61,7 +61,7 @@ export default { this.infinityUnlocked = progress.isInfinityUnlocked; this.eternityUnlocked = progress.isEternityUnlocked; this.realityUnlocked = progress.isRealityUnlocked; - this.alchemyUnlocked = Ra.has(RA_UNLOCKS.EFFARIG_UNLOCK); + this.alchemyUnlocked = Ra.unlocks.effarigUnlock.canBeApplied; const options = player.options.showHintText; this.achievements = options.achievements; diff --git a/src/components/modals/options/NewsOptionsModal.vue b/src/components/modals/options/NewsOptionsModal.vue index 7b5a783ee..6b0c4d3e9 100644 --- a/src/components/modals/options/NewsOptionsModal.vue +++ b/src/components/modals/options/NewsOptionsModal.vue @@ -1,14 +1,16 @@ @@ -67,36 +108,30 @@ export default {
{{ formatInt(parseInt(repeatBuffer)) }} message repeat buffer - + v-bind="sliderPropsRepeatBuffer" + :value="repeatBuffer" + @input="adjustSliderValueRepeatBuffer($event)" + />
{{ formatPercents(parseFloat(AIChance)) }} AI messages - + v-bind="sliderPropsAIChance" + :value="AIChance" + @input="adjustSliderValueAIChance($event)" + />
{{ formatPercents(parseFloat(speed)) }} scroll speed - + v-bind="sliderPropsSpeed" + :value="speed" + @input="adjustSliderValueSpeed($event)" + />
+export const GlyphInfo = { + types: { + NONE: 0, + LEVEL: 1, + RARITY: 2, + SAC_VALUE: 3, + FILTER_SCORE: 4, + CURRENT_REFINE: 5, + MAX_REFINE: 6, + }, + labels: ["None", "Level", "Rarity", "Sacrifice Value", "Glyph Filter Score", + "Current Refinement Value", "Maximum Refinement Value"] +}; + + +export default { + name: "SelectGlyphInfoDropdown", + computed: { + availableTypes() { + const typeEnum = GlyphInfo.types; + const options = [typeEnum.NONE, typeEnum.LEVEL, typeEnum.RARITY]; + if (GlyphSacrificeHandler.canSacrifice) options.push(typeEnum.SAC_VALUE); + if (EffarigUnlock.glyphFilter.isUnlocked) options.push(typeEnum.FILTER_SCORE); + if (Ra.unlocks.unlockGlyphAlchemy.canBeApplied) { + options.push(typeEnum.CURRENT_REFINE); + options.push(typeEnum.MAX_REFINE); + } + return options; + } + }, + methods: { + setType(type) { + player.options.showHintText.glyphInfoType = type; + EventHub.dispatch(GAME_EVENT.GLYPH_VISUAL_CHANGE); + }, + getType(type) { + return GlyphInfo.labels[type]; + } + } +}; + + + diff --git a/src/components/modals/options/hidden-tabs/HiddenSubtabsButton.vue b/src/components/modals/options/hidden-tabs/HiddenSubtabsButton.vue index 427c8e167..6d6c54327 100644 --- a/src/components/modals/options/hidden-tabs/HiddenSubtabsButton.vue +++ b/src/components/modals/options/hidden-tabs/HiddenSubtabsButton.vue @@ -9,6 +9,10 @@ export default { tab: { type: Object, required: true + }, + changeEnabled: { + type: Boolean, + required: true } }, data() { @@ -31,6 +35,9 @@ export default { [`c-hide-modal-tab-button--${this.tab.key}`]: !this.isCurrentSubtab, }; }, + isModernUI() { + return this.$viewModel.newUI; + }, }, methods: { update() { @@ -38,6 +45,7 @@ export default { this.hidden = this.subtab.isHidden && this.hidable; }, toggleVisibility() { + if (!this.changeEnabled) return; this.subtab.toggleVisibility(); }, }, @@ -50,6 +58,33 @@ export default { :class="classObject" @click="toggleVisibility" > - {{ subtab.name }} +
+
+
+ {{ subtab.name }} +
+
+ + diff --git a/src/components/modals/options/hidden-tabs/HiddenTabGroup.vue b/src/components/modals/options/hidden-tabs/HiddenTabGroup.vue index 31763ee3a..246ca759d 100644 --- a/src/components/modals/options/hidden-tabs/HiddenTabGroup.vue +++ b/src/components/modals/options/hidden-tabs/HiddenTabGroup.vue @@ -11,6 +11,10 @@ export default { type: Object, required: true }, + changeEnabled: { + type: Boolean, + required: true + } }, data() { return { @@ -27,11 +31,6 @@ export default { subtabs() { return this.tab.subtabs; }, - styleObjectRow() { - return { - "background-color": this.isHidden ? "var(--color-gh-purple)" : "var(--color-good)", - }; - }, isCurrentTab() { return this.tab.id === Tabs.current.id; }, @@ -71,6 +70,7 @@ export default { this.unlockedSubtabs = this.subtabs.filter(sub => sub.isUnlocked); }, toggleVisibility() { + if (!this.changeEnabled) return; // If this tab and all unlocked subtabs are hidden, unhide all subtabs in addition to the tab if (this.tab.isHidden && this.unlockedSubtabs.every(t => t.isHidden)) { for (const subtab of this.unlockedSubtabs) { @@ -96,6 +96,7 @@ export default { :key="i" :subtab="subtab" :tab="tab" + :change-enabled="changeEnabled" />
.c-indicator-icon { - color: black; + width: 2rem; position: absolute; - right: 0; top: 0; - padding: 0.2rem; + right: 0; + color: black; text-shadow: none; + padding: 0.2rem; } .c-hidden-tabs-background__visible { diff --git a/src/components/modals/options/hidden-tabs/HiddenTabsModal.vue b/src/components/modals/options/hidden-tabs/HiddenTabsModal.vue index 4d510424f..41a16b831 100644 --- a/src/components/modals/options/hidden-tabs/HiddenTabsModal.vue +++ b/src/components/modals/options/hidden-tabs/HiddenTabsModal.vue @@ -11,7 +11,7 @@ export default { data() { return { isEnslaved: false, - isDoomed: false, + isAlmostEnd: false, }; }, computed: { @@ -20,7 +20,7 @@ export default { methods: { update() { this.isEnslaved = Enslaved.isRunning; - this.isDoomed = Pelle.isDoomed; + this.isAlmostEnd = Pelle.hasGalaxyGenerator; }, }, }; @@ -38,8 +38,8 @@ export default { Unhiding a tab in which all subtabs are hidden will also unhide all subtabs, and hiding all subtabs will also hide the tab.
-
- You cannot hide your tabs within Doomed. +
+ You cannot hide your tabs after unlocking the Galaxy Generator.

@@ -51,6 +51,7 @@ export default { v-for="(tab, index) in tabs" :key="index" :tab="tab" + :change-enabled="!isEnslaved && !isAlmostEnd" class="l-hide-modal-tab-container" /> @@ -58,6 +59,6 @@ export default { \ No newline at end of file diff --git a/src/components/modals/prestige/AntimatterGalaxyModal.vue b/src/components/modals/prestige/AntimatterGalaxyModal.vue index 293a6c805..27a3f798c 100644 --- a/src/components/modals/prestige/AntimatterGalaxyModal.vue +++ b/src/components/modals/prestige/AntimatterGalaxyModal.vue @@ -7,8 +7,8 @@ export default { ModalWrapperChoice }, props: { - modalConfig: { - type: Object, + bulk: { + type: Boolean, required: true, } }, @@ -17,11 +17,10 @@ export default { newGalaxies: 0, keepAntimatter: false, perkANRBought: false, - keepDimBoost: false, + keepDimBoost: false }; }, computed: { - bulk() { return this.modalConfig.bulk; }, topLabel() { if (this.bulk) return `You are about to purchase ${quantifyInt("Antimatter Galaxy", this.newGalaxies)}`; return `You are about to purchase an Antimatter Galaxy`; @@ -54,9 +53,6 @@ export default { created() { this.on$(GAME_EVENT.DIMBOOST_AFTER, () => (BreakInfinityUpgrade.autobuyMaxDimboosts.isBought ? undefined : this.emitClose())); - this.on$(GAME_EVENT.BIG_CRUNCH_AFTER, this.emitClose); - this.on$(GAME_EVENT.ETERNITY_RESET_AFTER, this.emitClose); - this.on$(GAME_EVENT.REALITY_RESET_AFTER, this.emitClose); }, methods: { update() { @@ -72,13 +68,15 @@ export default { } } this.keepAntimatter = Achievement(111).isUnlocked; - this.perkANRBought = Perk.antimatterNoReset.isBought; - this.keepDimBoost = Achievement(143).isUnlocked; + this.perkANRBought = Perk.antimatterNoReset.canBeApplied; + this.keepDimBoost = (Achievement(143).isUnlocked && !Pelle.isDoomed) || + PelleUpgrade.galaxyNoResetDimboost.canBeApplied; }, handleYesClick() { requestGalaxyReset(this.bulk); Tutorial.turnOffEffect(TUTORIAL_STATE.GALAXY); - }, + EventHub.ui.offAll(this); + } }, }; diff --git a/src/components/modals/prestige/ArmageddonModal.vue b/src/components/modals/prestige/ArmageddonModal.vue index d7407ef79..9e32dcd08 100644 --- a/src/components/modals/prestige/ArmageddonModal.vue +++ b/src/components/modals/prestige/ArmageddonModal.vue @@ -28,7 +28,7 @@ export default { return `Armageddon will start a new Doomed Reality. You will gain ${quantify("Remnant", this.remnantsGain, 2, 0)} ${isFirstReset}`; - }, + } }, methods: { update() { @@ -46,8 +46,8 @@ export default { Glyphs.harshAutoClean(); if (!Glyphs.unequipAll()) { - Modal.message.show(`Entering Doomed will unequip your Glyphs. Some of your - Glyphs could not be unequipped due to lack of inventory space.`); + Modal.message.show(`Dooming your Reality will unequip your Glyphs. Some of your + Glyphs could not be unequipped due to lack of inventory space.`, 1); return; } Glyphs.harshAutoClean(); @@ -62,14 +62,31 @@ export default { disChargeAll(); player.buyUntil10 = true; player.records.realTimeDoomed = 0; - Pelle.quotes.show(Pelle.quotes.INITIAL); + for (const res of AlchemyResources.all) res.amount = 0; + AutomatorBackend.stop(); + + // Force-unhide all tabs except for the shop tab, for which we retain the hide state instead + const shopTab = ~1 & (1 << GameDatabase.tabs.find(t => t.key === "shop").id); + player.options.hiddenTabBits &= shopTab; + + // Force unhide MOST subtabs, although some of the tabs get ignored since they don't contain any + // meaningful interactable gameplay elements in Doomed + const tabsToIgnore = ["statistics", "achievements", "reality", "celestials"]; + const ignoredIDs = GameDatabase.tabs.filter(t => tabsToIgnore.includes(t.key)).map(t => t.id); + for (let tabIndex = 0; tabIndex < GameDatabase.tabs.length; tabIndex++) { + player.options.hiddenSubtabBits[tabIndex] &= ignoredIDs.includes(tabIndex) ? -1 : 0; + } + Pelle.quotes.initial.show(); }, }, }; diff --git a/src/components/tabs/automator/AutomatorEditor.vue b/src/components/tabs/automator/AutomatorEditor.vue index 55d201e0c..58026cdbe 100644 --- a/src/components/tabs/automator/AutomatorEditor.vue +++ b/src/components/tabs/automator/AutomatorEditor.vue @@ -1,8 +1,7 @@ diff --git a/src/components/tabs/automator/AutomatorErrorPage.vue b/src/components/tabs/automator/AutomatorErrorPage.vue index f7714b317..84598dca2 100644 --- a/src/components/tabs/automator/AutomatorErrorPage.vue +++ b/src/components/tabs/automator/AutomatorErrorPage.vue @@ -11,8 +11,8 @@ export default { this.errors = AutomatorData.currentErrors(); }, scrollToLine(line) { - AutomatorTextUI.scrollToLine(line - 1); - AutomatorTextUI.updateHighlightedLine(line, "Error"); + AutomatorScroller.scrollToLine(line); + AutomatorHighlighter.updateHighlightedLine(line, LineEnum.Error); } } }; @@ -33,7 +33,7 @@ export default { On line {{ error.startLine }}:
diff --git a/src/components/tabs/black-hole/BlackHoleTab.vue b/src/components/tabs/black-hole/BlackHoleTab.vue index 935cdfb82..b49f8a729 100644 --- a/src/components/tabs/black-hole/BlackHoleTab.vue +++ b/src/components/tabs/black-hole/BlackHoleTab.vue @@ -1,9 +1,9 @@