Merge branch 'master' into earth/add-unsupported-browser-message

This commit is contained in:
earth 2022-03-27 18:57:15 -05:00
commit 56c2712dc6
222 changed files with 27382 additions and 2494 deletions

View File

@ -33,11 +33,11 @@
}
],
"vue/one-component-per-file": "off",
"vue/component-definition-name-casing": "off",
"vue/order-in-components": "off",
"vue/require-prop-type-constructor": "off",
"vue/require-default-prop": "off",
"vue/one-component-per-file": "error",
"vue/component-definition-name-casing": "warn",
"vue/order-in-components": "warn",
"vue/require-prop-type-constructor": "warn",
"vue/require-default-prop": "warn",
"vue/html-comment-content-newline": "warn",
"vue/html-comment-content-spacing": "warn",
"vue/html-comment-indent": "warn",

View File

@ -1,52 +0,0 @@
import "./blob-snowflake";
import "./blob-background";
Vue.component("blob-snowflakes", {
data() {
return {
animateBackground: false,
count: 0,
initialized: false,
bounds: {
x: 0,
y: 0
}
};
},
mounted() {
this.updateSize();
window.addEventListener("resize", this.updateSize);
this.initialized = true;
},
destroyed() {
window.removeEventListener("resize", this.updateSize);
},
methods: {
update() {
this.animateBackground = player.options.animations.background;
this.count = player.options.animations.blobSnowflakes;
},
updateSize() {
this.bounds.x = this.$el.clientWidth;
this.bounds.y = this.$el.clientHeight;
}
},
template: `
<svg v-if="animateBackground" class="c-blob-snowflake-container">
<blob-snowflake
v-for="i in count"
v-if="initialized"
:key="i"
:bounds="bounds"
/>
</svg>
<svg v-else class="c-blob-background-container">
<blob-background
v-for="i in count"
v-if="initialized"
:key="i"
:bounds="bounds"
/>
</svg>`
});

View File

@ -1,67 +0,0 @@
import "./old-ui/old-ui.js";
import "./new-ui/new-ui.js";
import "./save-timer.js";
import "./speedrun-status.js";
import "./help-me.js";
import "./tt-shop.js";
import "./new-ui/sidebar.js";
import "./background-animations";
import TabComponents from "@/components/tabs";
import PopupModal from "@/components/modals/PopupModal";
import FadeToBlack from "@/components/tabs/celestial-pelle/FadeToBlack";
import CreditsContainer from "@/components/tabs/celestial-pelle/CreditsContainer";
import NewGame from "@/components/tabs/celestial-pelle/NewGame";
Vue.component("game-ui", {
components: {
...TabComponents,
PopupModal,
FadeToBlack,
CreditsContainer,
NewGame
},
computed: {
view() {
return this.$viewModel;
},
uiLayout() {
return this.view.newUI ? "new-ui" : "old-ui";
},
containerClass() {
return this.view.newUI ? "new-ui" : "old-ui";
},
page() {
const subtab = Tabs.current[this.$viewModel.subtab];
return subtab.config.component;
},
themeCss() {
return `stylesheets/theme-${this.view.theme}.css`;
}
},
template: `
<div
v-if="view.initialized"
id="ui-container"
:class="containerClass"
style="display: flex; justify-content: center;"
>
<div id="ui" class="c-game-ui">
<component :is="uiLayout">
<component :is="page" />
</component>
<PopupModal v-if="view.modal.current" :modal="view.modal.current" />
<modal-progress-bar v-if="view.modal.progressBar" />
<link v-if="view.theme !== 'Normal'" type="text/css" rel="stylesheet" :href="themeCss">
<help-me />
<background-animations />
</div>
<div id="notification-container" class="l-notification-container" />
<tt-shop v-if="view.subtab === 'studies'" class="l-time-studies-tab__tt-shop" />
<sidebar v-if="view.newUI" />
<save-timer />
<speedrun-status />
<FadeToBlack />
<CreditsContainer />
<NewGame />
</div>`
});

View File

@ -1,3 +0,0 @@
Vue.component("help-me", {
template: `<div onclick="Modal.h2p.show()" class="o-tab-btn l-help-me">?</div>`
});

View File

@ -1,4 +0,0 @@
import "./shop/shop-tab.js";
import "./modals/index.js";
import "./game-ui.js";

View File

@ -1 +0,0 @@
import "./modal-progress-bar.js";

View File

@ -1,91 +0,0 @@
import PrimaryButton from "@/components/PrimaryButton";
Vue.component("modal-progress-bar", {
components: {
"offline-speedup-button": {
components: {
PrimaryButton
},
props: {
button: Object,
progress: Object,
},
computed: {
canBeClicked() {
return this.button.condition(this.progress.current, this.progress.max);
},
buttonClass() {
return {
"o-primary-btn--width-medium": true,
"o-primary-btn--disabled": !this.canBeClicked,
};
}
},
methods: {
buttonClicked() {
if (!this.canBeClicked) return;
this.button.click();
}
},
template: `
<PrimaryButton
:class="buttonClass"
@click="buttonClicked"
>
{{ button.text }}
</PrimaryButton>`
}
},
computed: {
progress() {
return this.$viewModel.modal.progressBar;
},
foregroundStyle() {
return {
width: `${this.progress.current / this.progress.max * 100}%`,
};
},
remaining() {
const timeSinceStart = Date.now() - this.progress.startTime;
return formatFloat(
TimeSpan.fromMilliseconds(timeSinceStart / (this.progress.current / this.progress.max)).totalSeconds -
TimeSpan.fromMilliseconds(timeSinceStart).totalSeconds
, 1);
},
buttons() {
return this.progress.buttons || [];
}
},
template: `
<div class="l-modal-overlay c-modal-overlay" style="z-index: 8">
<div class="l-modal-progress-bar c-modal">
<div class="c-modal-progress-bar__label">
{{ progress.label }}
</div>
<div>
{{ progress.info() }}
</div>
<br>
<div>
{{ progress.progressName }}: {{ formatInt(progress.current) }}/{{ formatInt(progress.max) }}
</div>
<div>
Remaining: {{ remaining }} seconds
</div>
<div class="l-modal-progress-bar__hbox">
<div class="l-modal-progress-bar__bg c-modal-progress-bar__bg">
<div class="l-modal-progress-bar__fg c-modal-progress-bar__fg" :style="foregroundStyle" />
</div>
</div>
<br>
<div class="l-modal-progress-bar__buttons">
<offline-speedup-button
v-for="(button, id) in buttons"
:key="id"
:button="button"
:progress="progress"
/>
</div>
</div>
</div>`,
});

View File

@ -1,81 +0,0 @@
Vue.component("sidebar-currency", {
data() {
return {
AM: new Decimal(0),
IP: new Decimal(0),
EP: new Decimal(0),
RM: new Decimal(0),
IM: 0,
RS: new Decimal(0),
machineStr: "",
showIP: false,
showEP: false,
showRM: false,
showRS: false,
};
},
methods: {
update() {
this.AM.copyFrom(Currency.antimatter);
this.IP.copyFrom(Currency.infinityPoints);
this.EP.copyFrom(Currency.eternityPoints);
this.RM.copyFrom(Currency.realityMachines);
this.IM = Currency.imaginaryMachines.value;
this.RS.copyFrom(Currency.realityShards);
this.machineStr = formatMachines(this.RM, this.IM);
this.showIP = PlayerProgress.infinityUnlocked();
this.showEP = PlayerProgress.eternityUnlocked();
this.showRM = PlayerProgress.realityUnlocked();
this.showRS = Pelle.isDoomed;
}
},
template: `
<div class="resource">
<template v-if="showRS">
<h2 class="o-sidebar-currency--pelle">{{ format(RS, 2) }}</h2>
<div class="resource-information">
<span class="resource-name">{{ pluralize("Reality Shard", RS) }}</span>
</div>
</template>
<template v-else-if="showRM">
<template v-if="IM === 0">
<h2 class="o-sidebar-currency--reality">{{ format(RM, 2) }}</h2>
<div class="resource-information">
<span class="resource-name">{{ pluralize("Reality Machine", RM) }}</span>
</div>
</template>
<template v-else>
<h3 class="o-sidebar-currency--reality">
{{ machineStr }}
</h3>
<div class="resource-information">
<span class="resource-name">Machines</span>
</div>
</template>
</template>
<template v-else-if="showEP">
<h2 class="o-sidebar-currency--eternity">
{{ format(EP, 2) }}
</h2>
<div class="resource-information">
<span class="resource-name">{{ pluralize("Eternity Point", EP) }}</span>
</div>
</template>
<template v-else-if="showIP">
<h2 class="o-sidebar-currency--infinity">
{{ format(IP, 2) }}
</h2>
<div class="resource-information">
<span class="resource-name">{{ pluralize("Infinity Point", IP) }}</span>
</div>
</template>
<template v-else>
<h2 class="o-sidebar-currency--antimatter">
{{ format(AM, 2, 1) }}
</h2>
<div class="resource-information">
<span class="resource-name">Antimatter</span>
</div>
</template>
</div>`
});

View File

@ -1,28 +0,0 @@
import "./sidebar-resources/sidebar-currency.js";
import "./tab-button.js";
Vue.component("sidebar", {
data() {
return {
isHidden: false,
};
},
computed: {
tabs: () => Tabs.newUI
},
methods: {
update() {
this.isHidden = AutomatorData.isEditorFullscreen;
},
},
template: `
<div class="sidebar" v-if="!isHidden">
<sidebar-currency />
<tab-button
v-for="(tab, tabPosition) in tabs"
:key="tab.name"
:tab="tab"
:tabPosition="tabPosition"
/>
</div>`
});

View File

@ -1,72 +0,0 @@
import "./game-header-eternity-button.js";
import "./game-header-new-dim-button.js";
import "./game-header-big-crunch-button.js";
import "./game-header-tickspeed-row.js";
import "./game-header-amounts-line.js";
import "../../header-challenge-display.js";
import BlackHoleHeaderRow from "@/components/BlackHoleHeaderRow";
Vue.component("game-header", {
components: {
BlackHoleHeaderRow
},
data() {
return {
isInEffarig: false,
effarigMultNerfText: "",
effarigTickNerfText: "",
isInLaitela: false,
laitelaTimer: 0,
laitelaEntropy: "",
antimatter: new Decimal(0),
antimatterPerSec: new Decimal(0)
};
},
methods: {
update() {
this.isInEffarig = Effarig.isRunning;
if (this.isInEffarig) {
this.effarigMultNerfText = `${formatPow(0.25 + 0.25 * Effarig.nerfFactor(Currency.infinityPower.value), 0, 5)}`;
this.effarigTickNerfText = `${formatPow(0.7 + 0.1 * Effarig.nerfFactor(Currency.timeShards.value), 0, 5)}`;
}
this.isInLaitela = Laitela.isRunning;
if (this.isInLaitela) {
if (player.celestials.laitela.entropy > 0) {
this.laitelaEntropy = `${formatPercents(player.celestials.laitela.entropy, 2, 2)}`;
this.laitelaTimer = Time.thisRealityRealTime.toStringShort();
} else {
this.laitelaEntropy = `${formatPercents(1, 2, 2)}`;
this.laitelaTimer = TimeSpan.fromSeconds(player.celestials.laitela.thisCompletion).toStringShort();
}
}
this.antimatter.copyFrom(Currency.antimatter);
this.antimatterPerSec.copyFrom(Currency.antimatter.productionPerSecond);
}
},
template: `
<div>
<header-challenge-display />
<div v-if="isInEffarig">
Gamespeed and multipliers are Dilated {{ effarigMultNerfText }}
<br>
Tickspeed is Dilated {{ effarigTickNerfText }}
</div>
<div v-if="isInLaitela">
Entropy: {{ laitelaEntropy }} ({{ laitelaTimer }})
</div>
<game-header-amounts-line />
<div>
<p>
You have <span class="c-game-header__antimatter">{{ format(antimatter, 2, 1) }}</span> antimatter.
</p>
</div>
<div class="l-game-header__buttons-line">
<game-header-big-crunch-button />
<game-header-new-dim-button />
<game-header-eternity-button />
</div>
<div>You are getting {{ format(antimatterPerSec, 2, 0) }} antimatter per second.</div>
<game-header-tickspeed-row />
<BlackHoleHeaderRow />
</div>`
});

View File

@ -1,24 +0,0 @@
import "./old-ui-subtab-button.js";
Vue.component("old-ui-subtab-bar", {
data() {
return {
isVisible: false
};
},
computed: {
tab: () => Tabs.current,
subtabs() {
return this.tab.subtabs;
}
},
methods: {
update() {
this.isVisible = this.subtabs.countWhere(subtab => subtab.isAvailable) > 1;
}
},
template: `
<div class="c-subtab-button-container" v-if="isVisible">
<old-ui-subtab-button v-for="(subtab, i) in subtabs" :key="i" :subtab="subtab" />
</div>`
});

View File

@ -1,26 +0,0 @@
Vue.component("old-ui-subtab-button", {
props: {
subtab: Object
},
data() {
return {
isAvailable: false,
hasNotification: false
};
},
methods: {
update() {
this.isAvailable = this.subtab.isAvailable;
this.hasNotification = this.subtab.hasNotification;
}
},
template: `
<button
v-if="isAvailable"
@click="subtab.show(true)"
class="o-tab-btn o-tab-btn--secondary"
>
{{ subtab.name }}
<i v-if="hasNotification" class="fas fa-exclamation"></i>
</button>`
});

View File

@ -1,11 +0,0 @@
import "./old-ui-tab-button.js";
Vue.component("old-ui-tab-bar", {
computed: {
tabs: () => Tabs.oldUI
},
template: `
<div>
<old-ui-tab-button v-for="(tab, i) in tabs" :key="i" :tabPosition="i" :tab="tab" />
</div>`
});

View File

@ -1,66 +0,0 @@
import "./old-ui-subtab-bar.js";
import "./old-ui-tab-bar.js";
import "./header/game-header.js";
import NewsTicker from "@/components/NewsTicker";
import FooterLinks from "@/components/FooterLinks";
import InfinityPointsHeader from "@/components/InfinityPointsHeader";
import EternityPointsHeader from "@/components/EternityPointsHeader";
import RealityMachinesHeader from "@/components/RealityMachinesHeader";
Vue.component("old-ui", {
components: {
"big-crunch-button": {
template: `<button class="o-tab-btn o-big-crunch-btn" onclick="bigCrunchResetRequest()">Big Crunch</button>`
},
NewsTicker,
FooterLinks,
InfinityPointsHeader,
EternityPointsHeader,
RealityMachinesHeader
},
data() {
return {
bigCrunch: false,
smallCrunch: false
};
},
computed: {
tab: () => Tabs.current,
news() {
return this.$viewModel.news;
}
},
methods: {
update() {
const crunchButtonVisible = !player.break && Player.canCrunch;
const reachedInfinityInMinute = Time.bestInfinityRealTime.totalMinutes <= 1;
this.bigCrunch = crunchButtonVisible && !reachedInfinityInMinute;
this.smallCrunch = crunchButtonVisible && reachedInfinityInMinute;
}
},
template: `
<div id="container" class="container c-old-ui l-old-ui">
<link rel="stylesheet" type="text/css" href="stylesheets/old-ui.css">
<template v-if="bigCrunch">
<big-crunch-button class="l-old-ui__big-crunch-btn" />
<div class="o-emptiness">
The world has collapsed on itself due to excess of antimatter.
</div>
</template>
<template v-else>
<NewsTicker class="l-old-ui__news-bar" v-if="news" />
<game-header class="l-old-ui__header" />
<old-ui-tab-bar />
<component v-if="tab.config.before" :is="tab.config.before" />
<old-ui-subtab-bar />
<big-crunch-button
v-show="smallCrunch"
class="l-old-ui__big-crunch-btn l-old-ui__big-crunch-btn--overlay"
/>
<div class="l-old-ui-page l-old-ui__page">
<slot />
</div>
<FooterLinks class="l-old-ui__footer" />
</template>
</div>`
});

View File

@ -1,28 +0,0 @@
Vue.component("save-timer", {
data() {
return {
currentTime: 0,
lastSave: 0,
showTimeSinceSave: false,
};
},
computed: {
time() {
return timeDisplayShort(this.currentTime - this.lastSave);
},
},
methods: {
update() {
this.lastSave = GameStorage.lastSaveTime;
this.currentTime = Date.now();
this.showTimeSinceSave = player.options.showTimeSinceSave;
},
save() {
GameStorage.save();
}
},
template: `
<div class="o-save-timer" v-if="showTimeSinceSave" @click="save">
Time since last save: {{ time }}
</div>`
});

View File

@ -1,34 +0,0 @@
Vue.component("shop-button", {
props: {
purchase: Object,
},
data() {
return {
currentMult: 0,
nextMult: 0
};
},
methods: {
update() {
this.currentMult = this.purchase.currentMult;
this.nextMult = this.purchase.nextMult;
}
},
template: `
<div class="c-shop-button-container">
<div class="o-shop-button-description">
{{ purchase.description }}
<br>
<span class="o-shop-button-multiplier">
Currently {{ formatX(currentMult, 2, 0) }}, next: {{ formatX(nextMult, 2, 0) }}
</span>
</div>
<button
@click="purchase.purchase()"
class="o-shop-button-button"
>
Cost: {{ purchase.cost }}
<img src="images/std_coin.png" height="40">
</button>
</div>`
});

View File

@ -1,71 +0,0 @@
import "./shop-button.js";
Vue.component("shop-tab", {
data() {
return {
STD: 0,
kongEnabled: false,
};
},
computed: {
purchases() {
return ShopPurchase.all;
},
buySTDText() {
return this.kongEnabled ? "Buy More" : "Play in Kongregate to buy STDs";
}
},
methods: {
update() {
this.STD = player.IAP.totalSTD - player.IAP.spentSTD;
this.kongEnabled = kong.enabled;
},
showStore() {
if (!this.kongEnabled) return;
Modal.shop.show();
},
buyTimeSkip() {
kong.purchaseTimeSkip(10);
},
buyLongerTimeSkip() {
kong.purchaseLongerTimeSkip(20);
},
},
template: `
<div id="shop" class="tab">
<div class="c-shop-disclaimer">
Disclaimer: These are not required to progress in the game, they are just for supporting the developer.
The game is balanced without the use of any microtranactions.
</div>
<div class="c-shop-header">
<span>You have {{ STD }}</span>
<img src="images/std_coin.png" height="40">
<button class="o-shop-button-button" @click="showStore()">{{ buySTDText }}</button>
</div>
<div class="l-shop-buttons-container">
<shop-button v-for="purchase in purchases" :purchase="purchase" :key="purchase.key"></shop-button>
<div class="c-shop-button-container">
<div class="o-shop-button-description">
Get 6 hours worth of offline production. (Autobuyers don't work full speed)
</div>
<button
@click="buyTimeSkip()"
class="o-shop-button-button"
>
Cost: 10 <img src="images/std_coin.png" height="40">
</button>
</div>
<div class="c-shop-button-container">
<div class="o-shop-button-description">
Get 24 hours worth of offline production. (Autobuyers don't work full speed)
</div>
<button
@click="buyLongerTimeSkip()"
class="o-shop-button-button"
>
Cost: 20 <img src="images/std_coin.png" height="40">
</button>
</div>
</div>
</div>`
});

View File

@ -1,292 +0,0 @@
import "./hover-menu.js";
import PrimaryToggleButton from "@/components/PrimaryToggleButton";
Vue.component("tt-shop", {
components: {
PrimaryToggleButton
},
data() {
return {
theoremAmount: new Decimal(0),
theoremGeneration: new Decimal(0),
totalTimeTheorems: new Decimal(0),
shopMinimized: false,
minimizeAvailable: false,
hasTTAutobuyer: false,
isAutobuyerOn: false,
budget: {
am: new Decimal(0),
ip: new Decimal(0),
ep: new Decimal(0)
},
costs: {
am: new Decimal(0),
ip: new Decimal(0),
ep: new Decimal(0)
},
showST: false,
STamount: 0,
showTTGen: false
};
},
watch: {
isAutobuyerOn(newValue) {
Autobuyer.timeTheorem.isActive = newValue;
}
},
computed: {
minimized() {
return this.minimizeAvailable && this.shopMinimized;
},
formatTimeTheoremType() {
if (this.theoremAmount.gte(1e6)) {
return format;
}
if (!(Teresa.isRunning || Enslaved.isRunning) &&
getAdjustedGlyphEffect("dilationTTgen") > 0 && !DilationUpgrade.ttGenerator.isBought) {
return formatFloat;
}
return formatInt;
},
TTgenRateText() {
if (this.theoremGeneration.lt(1 / 3600)) {
return `one TT every ${TimeSpan.fromSeconds(
this.theoremGeneration.reciprocal().toNumber()).toStringShort(false)}`;
}
if (this.theoremGeneration.lt(0.1)) {
return `${format(this.theoremGeneration.times(3600), 2, 2)} TT/hour`;
}
return `${format(this.theoremGeneration, 2, 2)} TT/sec`;
},
totalTimeTheoremText() {
return `${quantify("total Time Theorem", this.totalTimeTheorems, 2, 2)}`;
},
minimizeArrowStyle() {
return {
transform: this.minimized ? "rotateX(180deg)" : "",
};
},
saveLoadText() {
return this.$viewModel.shiftDown ? "save:" : "load:";
},
},
methods: {
minimize() {
player.timestudy.shopMinimized = !player.timestudy.shopMinimized;
},
formatAM(am) {
return `${format(am)} AM`;
},
buyWithAM() {
TimeTheorems.buyOne(false, "am");
},
formatIP(ip) {
return `${format(ip)} IP`;
},
buyWithIP() {
TimeTheorems.buyOne(false, "ip");
},
formatEP(ep) {
return `${format(ep, 2, 0)} EP`;
},
buyWithEP() {
TimeTheorems.buyOne(false, "ep");
},
buyMaxTheorems() {
TimeTheorems.buyMax(false);
},
update() {
this.theoremAmount.copyFrom(Currency.timeTheorems);
this.theoremGeneration.copyFrom(getTTPerSecond().times(getGameSpeedupFactor()));
this.totalTimeTheorems.copyFrom(Currency.timeTheorems.max);
this.shopMinimized = player.timestudy.shopMinimized;
this.hasTTAutobuyer = Autobuyer.timeTheorem.isUnlocked;
this.isAutobuyerOn = Autobuyer.timeTheorem.isActive;
this.minimizeAvailable = DilationUpgrade.ttGenerator.isBought || this.hasTTAutobuyer;
const budget = this.budget;
budget.am.copyFrom(TimeTheoremPurchaseType.am.currency);
budget.ip.copyFrom(TimeTheoremPurchaseType.ip.currency);
budget.ep.copyFrom(TimeTheoremPurchaseType.ep.currency);
const costs = this.costs;
costs.am.copyFrom(TimeTheoremPurchaseType.am.cost);
costs.ip.copyFrom(TimeTheoremPurchaseType.ip.cost);
costs.ep.copyFrom(TimeTheoremPurchaseType.ep.cost);
this.showST = V.spaceTheorems > 0 && !Pelle.isDoomed;
this.STamount = V.availableST;
this.showTTGen = this.theoremGeneration.gt(0) && !ui.view.shiftDown;
},
},
template: `
<div id="TTbuttons">
<div class="ttshop-container ttshop-background">
<div data-role="page" class="ttbuttons-row ttbuttons-top-row">
<button
class="l-tt-save-load-btn c-tt-buy-button c-tt-buy-button--unlocked"
onClick="Modal.preferredTree.show()"
>
<i class='fas fa-cog'></i>
</button>
<p id="timetheorems">
<span class="c-tt-amount">
{{ quantify("Time Theorem", theoremAmount, 2, 0, formatTimeTheoremType) }}
</span>
<span v-if="showST">
<br>
{{ quantifyInt("Space Theorem", STamount) }}
</span>
</p>
<div style="display: flex; flex-direction: column; align-items: left;">
<div style="display: flex; flex-direction: row; align-items: center;">
<span class="c-ttshop__save-load-text">{{ saveLoadText }}</span>
<tt-save-load-button v-for="saveslot in 6" :key="saveslot" :saveslot="saveslot"></tt-save-load-button>
</div>
<span v-if="showTTGen">
You are gaining {{ TTgenRateText }}.
</span>
<span v-else>
You have {{ totalTimeTheoremText }}.
</span>
</div>
</div>
<div class="ttbuttons-row" v-if="!minimized">
<tt-buy-button :budget="budget.am" :cost="costs.am" :formatCost="formatAM" :action="buyWithAM" />
<tt-buy-button :budget="budget.ip" :cost="costs.ip" :formatCost="formatIP" :action="buyWithIP" />
<tt-buy-button :budget="budget.ep" :cost="costs.ep" :formatCost="formatEP" :action="buyWithEP" />
<div class="l-tt-buy-max-vbox">
<button
v-if="!minimized"
class="o-tt-top-row-button c-tt-buy-button c-tt-buy-button--unlocked"
@click="buyMaxTheorems"
>
Buy max
</button>
<PrimaryToggleButton
v-if="!minimized && hasTTAutobuyer"
v-model="isAutobuyerOn"
class="o-tt-autobuyer-button c-tt-buy-button c-tt-buy-button--unlocked"
label="Auto:"
/>
</div>
</div>
</div>
<button v-if="minimizeAvailable" class="ttshop-minimize-btn ttshop-background" @click="minimize">
<span id="minimizeArrow" :style="minimizeArrowStyle"></span>
</button>
</div>`
});
Vue.component("tt-save-load-button", {
props: {
saveslot: Number
},
data() {
return {
name: player.timestudy.presets[this.saveslot - 1].name,
};
},
computed: {
preset() {
return player.timestudy.presets[this.saveslot - 1];
},
displayName() {
return this.name === "" ? this.saveslot : this.name;
}
},
methods: {
nicknameBlur(event) {
this.preset.name = event.target.value.slice(0, 4);
this.name = this.preset.name;
},
hideContextMenu() {
this.$viewModel.currentContextMenu = null;
},
save() {
this.hideContextMenu();
this.preset.studies = TimeStudyTree.formatStudyList(GameCache.currentStudyTree.value.exportString);
const presetName = this.name ? `Study preset "${this.name}"` : "Study preset";
GameUI.notify.eternity(`${presetName} saved in slot ${this.saveslot}`);
},
load() {
this.hideContextMenu();
if (this.preset.studies) {
// We need to use a combined tree for committing to the game state, or else it won't buy studies in the imported
// tree are only reachable if the current tree is already bought
const combinedTree = new TimeStudyTree();
combinedTree.attemptBuyArray(TimeStudyTree.currentStudies, false);
combinedTree.attemptBuyArray(combinedTree.parseStudyImport(this.preset.studies), true);
TimeStudyTree.commitToGameState(combinedTree.purchasedStudies);
const presetName = this.name ? `Study preset "${this.name}"` : "Study preset";
GameUI.notify.eternity(`${presetName} loaded from slot ${this.saveslot}`);
} else {
Modal.message.show("This Time Study list currently contains no Time Studies.");
}
},
handleExport() {
this.hideContextMenu();
copyToClipboard(this.preset.studies);
const presetName = this.name ? `Study preset "${this.name}"` : "Study preset";
GameUI.notify.eternity(`${presetName} exported from slot ${this.saveslot} to your clipboard`);
},
edit() {
Modal.studyString.show({ id: this.saveslot - 1 });
}
},
template: `
<hover-menu class="l-tt-save-load-btn__wrapper">
<template #object>
<button
class="l-tt-save-load-btn c-tt-buy-button c-tt-buy-button--unlocked"
@click.shift.exact="save"
@click.exact="load"
>
{{ displayName }}
</button>
</template>
<template #menu>
<div class="l-tt-save-load-btn__menu c-tt-save-load-btn__menu">
<span ach-tooltip="Set a custom name (up to 4 characters)">
<input
type="text"
size="4"
maxlength="4"
class="l-tt-save-load-btn__menu-rename c-tt-save-load-btn__menu-rename"
:value="name"
@keyup.esc="hideContextMenu"
@blur="nicknameBlur"
/>
</span>
<div class="l-tt-save-load-btn__menu-item c-tt-save-load-btn__menu-item" @click="edit">Edit</div>
<div class="l-tt-save-load-btn__menu-item c-tt-save-load-btn__menu-item" @click="handleExport">Export</div>
<div class="l-tt-save-load-btn__menu-item c-tt-save-load-btn__menu-item" @click="save">Save</div>
<div class="l-tt-save-load-btn__menu-item c-tt-save-load-btn__menu-item" @click="load">Load</div>
</div>
</template>
</hover-menu>`
});
Vue.component("tt-buy-button", {
props: ["budget", "cost", "formatCost", "action"],
computed: {
isEnabled() {
if (Pelle.isDoomed && player.eternities.eq(0)) return false;
return this.budget.gte(this.cost);
},
enabledClass() {
if (!this.isEnabled || this.isLimitedByPelle) return "c-tt-buy-button--locked";
return "c-tt-buy-button--unlocked";
},
isLimitedByPelle() {
return Pelle.isDoomed && player.eternities.eq(0)
}
},
template: `
<button
class="l-tt-buy-button c-tt-buy-button"
:class="enabledClass"
@click="action"
>
{{ isLimitedByPelle ? "Locked in Doomed before Eternity" : formatCost(cost) }}
</button>`
});

View File

@ -39,6 +39,7 @@ import PurgeAllUnprotectedGlyphsModal from "@/components/modals/glyph-management
import PurgeAllRejectedGlyphsModal from "@/components/modals/glyph-management/PurgeAllRejectedGlyphsModal";
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";
@ -170,6 +171,7 @@ 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.awayProgress = new Modal(AwayProgressModal);
Modal.loadGame = new Modal(LoadGameModal);
Modal.import = new Modal(ImportSaveModal);

View File

@ -11,6 +11,9 @@ export class GameOptions {
static toggleUI() {
player.options.newUI = !player.options.newUI;
ui.view.newUI = player.options.newUI;
// This is needed because .s-base--dark is on newUI/normal but not on oldUI/normal
// So the classes on body need to be updated
Themes.find(player.options.theme).set();
GameStorage.save(true);
}

View File

@ -134,7 +134,7 @@ export const Themes = {
Theme.create("S4", { secret: true, }),
Theme.create("S5", { secret: true, }),
Theme.create("S6", { dark: true, animated: true, secret: true, }),
Theme.create("S7", { secret: true, }),
Theme.create("S7", { metro: true, secret: true, }),
Theme.create("S8", { metro: true, secret: true, }),
Theme.create("S9", { secret: true, }),
Theme.create("S10", { dark: true, metro: true, animated: true, secret: true, }),

View File

@ -3,6 +3,7 @@ import { state } from "./ui.init.js";
import VTooltip from "v-tooltip";
import { useLongPress, useRepeatingClick } from "./longpress";
import VueGtag from "vue-gtag";
import GameUIComponent from "@/components/GameUIComponent";
Vue.mixin({
computed: {
@ -10,6 +11,29 @@ Vue.mixin({
return state.view;
}
},
created() {
if (this.update) {
this.on$(GAME_EVENT.UPDATE, this.update);
if (GameUI.initialized) {
this.update();
}
}
// Following is used to force the recomputation of computed values
// from this fiddle https://codepen.io/sirlancelot/pen/JBeXeV
const recomputed = Object.create(null);
const watchers = this._computedWatchers;
if (!watchers) return;
for (const key in watchers) makeRecomputable(watchers[key], key, recomputed);
this.$recompute = key => recomputed[key] = !recomputed[key];
Vue.observable(recomputed);
},
destroyed() {
EventHub.ui.offAll(this);
},
methods: {
emitClick() {
this.$emit("click");
@ -47,30 +71,6 @@ Vue.mixin({
pluralize,
quantify,
quantifyInt
},
created() {
if (this.update) {
this.on$(GAME_EVENT.UPDATE, this.update);
if (GameUI.initialized) {
this.update();
}
}
// Following is used to force the recomputation of computed values
// from this fiddle https://codepen.io/sirlancelot/pen/JBeXeV
const recomputed = Object.create(null);
const watchers = this._computedWatchers;
if (!watchers) return;
for (const key in watchers)
makeRecomputable(watchers[key], key, recomputed);
this.$recompute = key => recomputed[key] = !recomputed[key];
Vue.observable(recomputed);
},
destroyed() {
EventHub.ui.offAll(this);
}
});
@ -185,6 +185,9 @@ Vue.use(VueGtag, {
export const ui = new Vue({
el: "#ui",
components: {
GameUIComponent
},
data: state,
computed: {
notation() {
@ -200,15 +203,6 @@ export const ui = new Vue({
return this.view.newUI;
},
},
methods: {
scroll(t) {
const now = Date.now();
if (this.view.scrollWindow) {
window.scrollBy(0, this.view.scrollWindow * (now - t) / 2);
setTimeout(() => this.scroll(now), 20);
}
}
},
watch: {
currentGlyphTooltip(newVal) {
if (newVal !== -1 && !GameUI.globalClickListener) {
@ -229,5 +223,14 @@ export const ui = new Vue({
}
},
},
template: "<game-ui />"
methods: {
scroll(t) {
const now = Date.now();
if (this.view.scrollWindow) {
window.scrollBy(0, this.view.scrollWindow * (now - t) / 2);
setTimeout(() => this.scroll(now), 20);
}
}
},
template: "<GameUIComponent />"
});

View File

@ -93,7 +93,7 @@ class AntimatterDimensionAutobuyerState extends UpgradeableAutobuyerState {
buyOneDimension(tier);
break;
case AUTOBUYER_MODE.BUY_10:
buyMaxDimension(tier, player.auto.bulkOn ? this.bulk : 1);
buyMaxDimension(tier, this.bulk);
break;
}
}
@ -129,9 +129,10 @@ class AntimatterDimensionAutobuyerState extends UpgradeableAutobuyerState {
static createAccessor() {
const accessor = super.createAccessor();
/** @returns {boolean} */
accessor.allBought = () => accessor.zeroIndexed.some(x => x.isBought);
accessor.allBought = () => accessor.zeroIndexed.every(x => x.isBought);
/** @returns {boolean} */
accessor.allUnlimitedBulk = () => accessor.zeroIndexed.some(x => x.hasUnlimitedBulk);
// 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;
return accessor;
}

View File

@ -88,25 +88,23 @@ Autobuyer.bigCrunch = new class BigCrunchAutobuyerState extends UpgradeableAutob
return PRESTIGE_EVENT.ETERNITY;
}
get willInfinity() {
if (!player.break || Player.isInAntimatterChallenge) return true;
switch (this.mode) {
case AUTO_CRUNCH_MODE.AMOUNT:
return gainedInfinityPoints().gte(this.amount);
case AUTO_CRUNCH_MODE.TIME:
return Time.thisInfinityRealTime.totalSeconds > this.time;
case AUTO_CRUNCH_MODE.X_HIGHEST:
default:
return gainedInfinityPoints().gte(player.records.thisEternity.maxIP.times(this.xHighest));
}
}
tick() {
super.tick();
let proc = !player.break || Player.isInAntimatterChallenge;
if (!proc) {
switch (this.mode) {
case AUTO_CRUNCH_MODE.AMOUNT:
proc = gainedInfinityPoints().gte(this.amount);
break;
case AUTO_CRUNCH_MODE.TIME:
proc = Time.thisInfinityRealTime.totalSeconds > this.time;
break;
case AUTO_CRUNCH_MODE.X_HIGHEST:
proc = gainedInfinityPoints().gte(player.records.thisEternity.maxIP.times(this.xHighest));
break;
}
}
if (proc) {
bigCrunchResetRequest(true);
}
if (this.willInfinity) bigCrunchResetRequest(true);
}
reset() {

View File

@ -70,20 +70,23 @@ Autobuyer.eternity = new class EternityAutobuyerState extends AutobuyerState {
}
}
tick() {
let proc = false;
get willEternity() {
// We Eternity asap if we're in an Eternity Challenge and can't reach more completions.
if (EternityChallenge.current?.gainedCompletionStatus.hasMoreCompletions === false) return true;
switch (this.mode) {
case AUTO_ETERNITY_MODE.AMOUNT:
proc = EternityChallenge.isRunning || gainedEternityPoints().gte(this.amount);
break;
return gainedEternityPoints().gte(this.amount);
case AUTO_ETERNITY_MODE.TIME:
proc = Time.thisEternityRealTime.totalSeconds > this.time;
break;
return Time.thisEternityRealTime.totalSeconds > this.time;
case AUTO_ETERNITY_MODE.X_HIGHEST:
proc = gainedEternityPoints().gte(player.records.thisReality.maxEP.times(this.xHighest));
break;
default:
return gainedEternityPoints().gte(player.records.thisReality.maxEP.times(this.xHighest));
}
if (proc) eternity(false, true);
}
tick() {
if (this.willEternity) eternity(false, true);
}
reset() {

View File

@ -163,7 +163,6 @@ export class AutomatorScript {
}
export const AutomatorData = {
documentationSubpage: 0,
// Used for getting the correct EC count in event log
lastECCompletionCount: 0,
// Used as a flag to make sure that wait commands only add one entry to the log instead of every execution attempt

View File

@ -94,6 +94,7 @@ import { AutomatorLexer } from "./lexer.js";
{ regex: /t[1-4]/ui, token: "number" },
{ 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" },
{ regex: /[a-zA-Z_][a-zA-Z_0-9]*/u, token: "variable", next: "commandDone" },
{ regex: /[1-9][0-9]+/ui, token: "number" },
],
@ -146,6 +147,7 @@ import { AutomatorLexer } from "./lexer.js";
{ regex: /(preset|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" },
{ regex: /x[\t ]+highest(\s|$)/ui, token: "variable-2" },
{ regex: /pending[\t ]+(completions|ip|ep|tp|rm|glyph[\t ]+level)(\s|$)/ui, token: "variable-2" },
{ regex: /total[\t ]+(completions|tt)(\s|$)/ui, token: "variable-2" },

View File

@ -625,25 +625,31 @@ export const AutomatorCommands = ((() => {
compile: ctx => {
const studies = ctx.$studies;
if (ctx.Nowait === undefined) return () => {
let prePurchasedStudies = 0;
let purchasedStudies = 0;
let finalPurchasedTS;
for (const tsNumber of studies.normal) {
if (TimeStudy(tsNumber).isBought) continue;
if (!TimeStudy(tsNumber).purchase()) {
if (purchasedStudies > 0) {
AutomatorData.logCommandEvent(`Purchased ${quantifyInt("Time Study", purchasedStudies)}
and stopped at study ${tsNumber}, waiting to attempt to purchase more studies`, ctx.startLine);
}
return AUTOMATOR_COMMAND_STATUS.NEXT_TICK_SAME_INSTRUCTION;
if (TimeStudy(tsNumber).isBought) prePurchasedStudies++;
else if (TimeStudy(tsNumber).purchase()) purchasedStudies++;
else finalPurchasedTS = finalPurchasedTS ?? tsNumber;
}
if (prePurchasedStudies + purchasedStudies < studies.normal.length) {
if (prePurchasedStudies + purchasedStudies === 0) {
AutomatorData.logCommandEvent(`Could not purchase any of the specified Time Studies`, ctx.startLine);
}
purchasedStudies++;
if (purchasedStudies > 0 && finalPurchasedTS) {
AutomatorData.logCommandEvent(`Purchased ${quantifyInt("Time Study", purchasedStudies)} and stopped at
Time Study ${finalPurchasedTS}, waiting to attempt to purchase more Time Studies`, ctx.startLine);
}
return AUTOMATOR_COMMAND_STATUS.NEXT_TICK_SAME_INSTRUCTION;
}
if (!studies.ec || TimeStudy.eternityChallenge(studies.ec).isBought) {
AutomatorData.logCommandEvent(`Purchased all specified time studies`, ctx.startLine);
AutomatorData.logCommandEvent(`Purchased all specified Time Studies`, ctx.startLine);
return AUTOMATOR_COMMAND_STATUS.NEXT_INSTRUCTION;
}
const unlockedEC = TimeStudy.eternityChallenge(studies.ec).purchase(true);
if (unlockedEC) {
AutomatorData.logCommandEvent(`Purchased all specified time studies and unlocked Eternity Challenge
AutomatorData.logCommandEvent(`Purchased all specified Time Studies and unlocked Eternity Challenge
${studies.ec}`, ctx.startLine);
return AUTOMATOR_COMMAND_STATUS.NEXT_INSTRUCTION;
}

View File

@ -230,6 +230,8 @@ export const AutomatorLexer = (() => {
createInCategory(StudyPath, "Active", /active/i, { $studyPath: TIME_STUDY_PATH.ACTIVE });
createInCategory(StudyPath, "Antimatter", /antimatter/i, { $studyPath: TIME_STUDY_PATH.ANTIMATTER_DIM });
createInCategory(StudyPath, "Time", /time/i, { $studyPath: TIME_STUDY_PATH.TIME_DIM });
createInCategory(StudyPath, "Light", /light/i, { $studyPath: TIME_STUDY_PATH.LIGHT });
createInCategory(StudyPath, "Dark", /dark/i, { $studyPath: TIME_STUDY_PATH.DARK });
createInCategory(TimeUnit, "Milliseconds", /ms/i, {
$autocomplete: "ms",

View File

@ -38,7 +38,13 @@ class PelleStrikeState extends GameMechanicState {
if (this.id === 5) {
Pelle.cel.records.totalAntimatter = new Decimal("1e180000");
Pelle.cel.records.totalInfinityPoints = new Decimal("1e60000");
Pelle.cel.records.totalEternityPoints = new Decimal("1e400");
Currency.eternityPoints.reset();
// Oddly specific number? Yes, it's roughly the amount of EP you have
// when starting dilation for the first time
// Since 5th strike previously did not reset your current EP the previous reset value was kind of useless which
// lead to some balancing problems, this hopefully prevents people starting dilation too early and getting
// softlocked, or starting it too late and getting not-softlocked.
Pelle.cel.records.totalEternityPoints = new Decimal("1e1050");
}
}

View File

@ -1,30 +1,38 @@
import { GameMechanicState } from "../../utils.js";
import { CelestialQuotes } from "../quotes.js";
class RaPetState {
/**
* @abstract
*/
get data() { throw new NotImplementedError(); }
class RaPetState extends GameMechanicState {
get data() {
return player.celestials.ra.pets[this.id];
}
/**
* @abstract
*/
get name() { throw new NotImplementedError(); }
get name() {
return this.config.name;
}
/**
* @abstract
*/
get chunkGain() { throw new NotImplementedError(); }
get chunkGain() {
return this.config.chunkGain;
}
/**
* @abstract
*/
get memoryGain() { throw new NotImplementedError(); }
get memoryGain() {
return this.config.memoryGain;
}
/**
* @abstract
*/
get requiredUnlock() { throw new NotImplementedError(); }
get color() {
return this.config.color;
}
get requiredUnlock() {
return this.config.requiredUnlock?.();
}
get rawMemoryChunksPerSecond() {
return this.config.rawMemoryChunksPerSecond();
}
get memoryProductionMultiplier() {
return this.config.memoryProductionMultiplier();
}
get isUnlocked() {
return this.requiredUnlock === undefined || Ra.has(this.requiredUnlock);
@ -62,16 +70,6 @@ class RaPetState {
return Ra.requiredMemoriesForLevel(this.level);
}
/**
* @abstract
*/
get rawMemoryChunksPerSecond() { throw new NotImplementedError(); }
/**
* @abstract
*/
get color() { throw new NotImplementedError(); }
get memoryChunksPerSecond() {
let res = this.canGetMemoryChunks ? this.rawMemoryChunksPerSecond : 0;
res *= RA_UNLOCKS.TT_BOOST.effect.memoryChunks();
@ -94,7 +92,6 @@ class RaPetState {
get chunkUpgradeCurrentMult() {
return Math.pow(1.5, this.data.chunkUpgrades);
}
get memoryUpgradeCost() {
@ -167,64 +164,10 @@ class RaPetState {
export const Ra = {
displayName: "Ra",
pets: {
teresa: new class TeresaRaPetState extends RaPetState {
get name() { return "Teresa"; }
get chunkGain() { return "Eternity Points"; }
get memoryGain() { return "current Reality Machines"; }
get data() { return player.celestials.ra.pets.teresa; }
get requiredUnlock() { return undefined; }
get rawMemoryChunksPerSecond() { return 4 * Math.pow(Currency.eternityPoints.value.pLog10() / 1e4, 3); }
get color() { return "#8596ea"; }
get memoryProductionMultiplier() {
return Ra.has(RA_UNLOCKS.TERESA_XP)
? 1 + Math.pow(Currency.realityMachines.value.pLog10() / 100, 0.5)
: 1;
}
}(),
effarig: new class EffarigRaPetState extends RaPetState {
get name() { return "Effarig"; }
get chunkGain() { return "Relic Shards gained"; }
get memoryGain() { return "best Glyph level"; }
get data() { return player.celestials.ra.pets.effarig; }
get requiredUnlock() { return RA_UNLOCKS.EFFARIG_UNLOCK; }
get rawMemoryChunksPerSecond() { return 4 * Math.pow(Effarig.shardsGained, 0.1); }
get color() { return "#ea8585"; }
get memoryProductionMultiplier() {
return Ra.has(RA_UNLOCKS.EFFARIG_XP)
? 1 + player.records.bestReality.glyphLevel / 7000
: 1;
}
}(),
enslaved: new class EnslavedRaPetState extends RaPetState {
get name() { return "Enslaved"; }
get chunkGain() { return "Time Shards"; }
get memoryGain() { return "total time played"; }
get data() { return player.celestials.ra.pets.enslaved; }
get requiredUnlock() { return RA_UNLOCKS.ENSLAVED_UNLOCK; }
get rawMemoryChunksPerSecond() { return 4 * Math.pow(Currency.timeShards.value.pLog10() / 3e5, 2); }
get color() { return "#f1aa7f"; }
get memoryProductionMultiplier() {
return Ra.has(RA_UNLOCKS.ENSLAVED_XP)
? 1 + Math.log10(player.records.totalTimePlayed) / 200
: 1;
}
}(),
v: new class VRaPetState extends RaPetState {
get name() { return "V"; }
get chunkGain() { return "Infinity Power"; }
get memoryGain() { return "total Memory levels"; }
get data() { return player.celestials.ra.pets.v; }
get requiredUnlock() { return RA_UNLOCKS.V_UNLOCK; }
get rawMemoryChunksPerSecond() { return 4 * Math.pow(Currency.infinityPower.value.pLog10() / 1e7, 1.5); }
get color() { return "#ead584"; }
get memoryProductionMultiplier() {
return Ra.has(RA_UNLOCKS.V_XP)
? 1 + Ra.totalPetLevel / 50
: 1;
}
}(),
},
pets: mapGameDataToObject(
GameDatabase.celestials.ra,
config => new RaPetState(config)
),
// Dev/debug function for easier testing
reset() {
const data = player.celestials.ra;
@ -575,11 +518,6 @@ export const GlyphAlteration = {
},
};
/**
* @type {RaPetState[]}
*/
Ra.pets.all = [Ra.pets.teresa, Ra.pets.effarig, Ra.pets.enslaved, Ra.pets.v];
export const RA_UNLOCKS = {
AUTO_TP: {
id: 0,

View File

@ -3,17 +3,22 @@ import { GameDatabase } from "./secret-formula/game-database";
class ConfirmationState {
constructor(config) {
this.name = config.name;
this.confirmationSetting = config.option;
this._confirmationSetting = config.option;
this.isUnlocked = config.isUnlocked;
}
get option() {
return player.options.confirmations[this.confirmationSetting];
return player.options.confirmations[this._confirmationSetting];
}
set option(value) {
player.options.confirmations[this.confirmationSetting] = value;
player.options.confirmations[this._confirmationSetting] = value;
}
}
export const ConfirmationTypes = GameDatabase.confirmationTypes.map(entry => new ConfirmationState(entry));
export const ConfirmationTypes = GameDatabase.confirmationTypes.mapToObject(
config => config.option,
config => new ConfirmationState(config)
);
ConfirmationTypes.index = Object.values(ConfirmationTypes);

View File

@ -108,7 +108,7 @@ dev.spin4d = function() {
dev.cancerize = function() {
Theme.tryUnlock("Cancer");
Notation.cancer.setAsCurrent();
Notation.emoji.setAsCurrent();
};
dev.fixSave = function() {

View File

@ -34,7 +34,8 @@ function giveEternityRewards(auto) {
AutomatorData.lastECCompletionCount = completionCount;
if (Enslaved.isRunning && completionCount > 5) EnslavedProgress.ec1.giveProgress();
}
player.etercreq = 0;
// eslint-disable-next-line no-bitwise
player.challenge.eternity.requirementBits &= ~(1 << challenge.id);
respecTimeStudies(auto);
}
@ -189,6 +190,9 @@ export class EternityMilestoneState {
}
get isReached() {
if (Pelle.isDoomed && this.config.pelleObsolete) {
return this.config.pelleObsolete();
}
return Currency.eternities.gte(this.config.eternities);
}
}

View File

@ -95,6 +95,7 @@ export class EternityChallengeState extends GameMechanicState {
get gainedCompletionStatus() {
const status = {
gainedCompletions: 0,
hasMoreCompletions: false,
totalCompletions: this.completions,
};
if (this.isFullyCompleted) return status;

View File

@ -123,7 +123,7 @@ function galaxyReset() {
player.galaxies++;
if (!Achievement(143).isUnlocked) player.dimensionBoosts = 0;
softReset(0);
if (Notations.current === Notation.cancer) player.requirementChecks.permanent.cancerGalaxies++;
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
player.requirementChecks.infinity.noSacrifice = true;
EventHub.dispatch(GAME_EVENT.GALAXY_RESET_AFTER);
@ -145,8 +145,8 @@ function maxBuyGalaxies(limit = Number.MAX_VALUE) {
const newGalaxies = Math.clampMax(
Galaxy.buyableGalaxies(Math.round(dim.totalAmount.toNumber())),
limit);
if (Notations.current === Notation.cancer) {
player.requirementChecks.permanent.cancerGalaxies += newGalaxies - player.galaxies;
if (Notations.current === Notation.emoji) {
player.requirementChecks.permanent.emojiGalaxies += newGalaxies - player.galaxies;
}
// Galaxy count is incremented by galaxyReset(), so add one less than we should:
player.galaxies = newGalaxies - 1;

View File

@ -687,12 +687,12 @@ export function getRarity(x) {
return GlyphRarities.find(e => x >= e.minStrength);
}
export function getAdjustedGlyphLevel(glyph) {
export function getAdjustedGlyphLevel(glyph, realityGlyphBoost = Glyphs.levelBoost) {
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 (BASIC_GLYPH_TYPES.includes(glyph.type)) return level + Glyphs.levelBoost;
if (BASIC_GLYPH_TYPES.includes(glyph.type)) return level + realityGlyphBoost;
return level;
}

View File

@ -158,7 +158,7 @@ export const shortcuts = [
},
visible: true
}, {
name: "Open the shortcut list",
name: "Open Hotkey List Modal",
keys: ["?"],
type: "bind",
function: () => {
@ -167,7 +167,7 @@ export const shortcuts = [
},
visible: true
}, {
name: "Open \"How to Play\" pop-up",
name: "Open How To Play Modal",
keys: ["h"],
type: "bind",
function: () => {
@ -194,7 +194,7 @@ export const shortcuts = [
},
visible: true
}, {
name: "Close pop-up or open options",
name: "Close Modal or open Options",
keys: ["esc"],
type: "bind",
function: () => {

View File

@ -46,13 +46,13 @@ window.player = {
eternity: {
current: 0,
unlocked: 0,
requirementBits: 0,
}
},
infinity: {
upgradeBits: 0
},
auto: {
bulkOn: true,
autobuyersOn: true,
disableContinuum: false,
reality: {
@ -232,7 +232,7 @@ window.player = {
slowestBH: 1,
},
permanent: {
cancerGalaxies: 0,
emojiGalaxies: 0,
singleTickspeed: 0,
perkTreeDragging: 0
}
@ -378,7 +378,6 @@ window.player = {
}),
},
eternityChalls: {},
etercreq: 0,
respec: false,
eterc8ids: 50,
eterc8repl: 40,
@ -425,6 +424,7 @@ window.player = {
glyphs: [],
}),
protectedRows: 2,
createdRealityGlyph: false,
},
seed: Math.floor(Date.now() * Math.random() + 1),
secondGaussian: 1e6,
@ -798,7 +798,6 @@ window.player = {
challenges: true,
eternity: true,
dilation: true,
reality: true,
resetReality: true,
glyphReplace: true,
glyphSacrifice: true,
@ -835,7 +834,8 @@ window.player = {
darkEnergy: true,
singularities: true,
celestialMemories: true,
blackHole: true
blackHole: true,
realityShards: true
},
hiddenTabBits: 0,
hiddenSubtabBits: Array.repeat(0, 11),

View File

@ -108,7 +108,7 @@ export function simulatedRealityCount(advancePartSimCounters) {
*/
export function requestManualReality() {
if (GlyphSelection.active || !isRealityAvailable()) return;
if (player.options.confirmations.reality || player.options.confirmations.glyphSelection) {
if (player.options.confirmations.glyphSelection) {
Modal.reality.show();
return;
}
@ -577,7 +577,7 @@ export function finishProcessReality(realityProps) {
player.reality.lastAutoEC = 0;
player.challenge.eternity.current = 0;
if (!PelleUpgrade.timeStudiesNoReset.canBeApplied) player.challenge.eternity.unlocked = 0;
player.etercreq = 0;
player.challenge.eternity.requirementBits = 0;
player.respec = false;
player.eterc8ids = 50;
player.eterc8repl = 40;

View File

@ -57,8 +57,8 @@ GameDatabase.achievements.secret = [
{
id: 22,
name: "Deep fried",
get description() { return `Buy ${formatInt(1e5)} Antimatter Galaxies in total while using cancer notation.`; },
checkRequirement: () => player.requirementChecks.permanent.cancerGalaxies >= 1e5,
get description() { return `Buy ${formatInt(1e5)} Antimatter Galaxies in total while using emoji notation.`; },
checkRequirement: () => player.requirementChecks.permanent.emojiGalaxies >= 1e5,
checkEvent: GAME_EVENT.GALAXY_RESET_AFTER
},
{

View File

@ -123,5 +123,9 @@ GameDatabase.awayProgressTypes = [
name: "singularities",
reference: ["celestials", "laitela", "singularities"],
isUnlocked: () => Laitela.isUnlocked,
}, {
name: "realityShards",
reference: ["celestials", "pelle", "realityShards"],
isUnlocked: () => Pelle.isDoomed,
},
];

View File

@ -566,7 +566,7 @@ GameDatabase.celestials.navigation = (function() {
clickAction: () => Tab.celestials.v.show(true),
incompleteClass: "c-celestial-nav__test-incomplete",
symbol: "⌬",
symbolOffset: "0.25",
symbolOffset: "2",
fill: "#ffe066",
position: Positions.vUnlockAchievement,
ring: {
@ -992,7 +992,7 @@ GameDatabase.celestials.navigation = (function() {
clickAction: () => Tab.celestials.ra.show(true),
incompleteClass: "c-celestial-nav__test-incomplete",
symbol: "\uf185",
symbolOffset: "0.25",
symbolOffset: "2",
fill: "#9063de",
position: Positions.raReality,
ring: {
@ -1300,7 +1300,7 @@ GameDatabase.celestials.navigation = (function() {
incompleteClass: "c-celestial-nav__test-incomplete",
symbol: "ᛝ",
symbolScale: 1.6,
symbolOffset: "0.1",
symbolOffset: "0.6",
fill: "white",
position: Positions.laitelaFirstCenter,
ring: {
@ -1384,7 +1384,7 @@ GameDatabase.celestials.navigation = (function() {
if (upgrade.isAvailableForPurchase) return [
dmdText,
`Imaginary Machines
${format(upgrade.currency.value.min(upgrade.cost), upgrade.canBeBought ? 0 : 2)}
${format(Math.min(upgrade.currency.value, upgrade.cost), upgrade.canBeBought ? 0 : 2)}
/ ${format(upgrade.cost)}`
];
@ -1455,7 +1455,7 @@ GameDatabase.celestials.navigation = (function() {
const upgrade = DarkMatterDimension(3).unlockUpgrade;
if (upgrade.canBeBought || upgrade.isBought) return 1;
if (upgrade.isAvailableForPurchase) return upgrade.currency.value / upgrade.cost;
if (!player.celestials.laitela.automation.singularity) return 0.5;
if (!player.auto.singularity.isActive) return 0.5;
return Math.clampMax(0.999, Singularity.singularitiesGained / 20);
},
node: {
@ -1482,11 +1482,11 @@ GameDatabase.celestials.navigation = (function() {
if (upgrade.isAvailableForPurchase) return [
dmdText,
`Imaginary Machines
${format(upgrade.currency.value.min(upgrade.cost), upgrade.canBeBought ? 0 : 2)}
${format(Math.min(upgrade.currency.value, upgrade.cost), upgrade.canBeBought ? 0 : 2)}
/ ${format(upgrade.cost)}`
];
if (!player.celestials.laitela.automation.singularity) return [
if (!player.auto.singularity.isActive) return [
dmdText,
"Unlock Automatic Singularities",
`${format(Currency.singularities.value)} / ${format(SingularityMilestone.autoCondense.start)}`
@ -1554,7 +1554,7 @@ GameDatabase.celestials.navigation = (function() {
if (upgrade.isAvailableForPurchase) return [
dmdText,
`Imaginary Machines
${format(upgrade.currency.value.min(upgrade.cost), upgrade.canBeBought ? 0 : 2)}
${format(Math.min(upgrade.currency.value, upgrade.cost), upgrade.canBeBought ? 0 : 2)}
/ ${format(upgrade.cost)}`
];
@ -1623,7 +1623,7 @@ GameDatabase.celestials.navigation = (function() {
incompleteClass: "c-celestial-nav__test-incomplete",
symbol: "ᛝ",
symbolScale: 1.6,
symbolOffset: "0.1",
symbolOffset: "0.6",
fill: "white",
position: Positions.laitelaThirdCenter,
ring: {

View File

@ -168,16 +168,16 @@ GameDatabase.celestials.pelle.upgrades = (function() {
cost: 1e26,
formatCost,
},
dilationUpgradesNoReset: {
dimBoostResetsNothing: {
id: 18,
description: "Keep Dilation Upgrades on Armageddon",
cost: 1e45,
description: "Dimension Boosts no longer reset anything",
cost: 1e30,
formatCost,
},
dimBoostResetsNothing: {
dilationUpgradesNoReset: {
id: 19,
description: "Dimension Boosts no longer reset anything",
cost: 1e50,
description: "Keep Dilation Upgrades on Armageddon",
cost: 1e45,
formatCost,
},
tachyonParticlesNoReset: {

View File

@ -0,0 +1,52 @@
import { GameDatabase } from "../game-database.js";
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)
},
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)
}
};

View File

@ -76,10 +76,9 @@ GameDatabase.celestials.pelle.rifts = {
{
resource: "pestilence",
requirement: 0.6,
effectCondition: () => Replicanti.amount.gt(DC.E1300),
description: () => `When Replicanti exceeds ${format(DC.E1300)},
all Galaxies are ${formatPercents(0.1)} more effective`,
effect: 1.1
effect: () => (Replicanti.amount.gt(DC.E1300) ? 1.1 : 1)
},
{
resource: "pestilence",

View File

@ -33,10 +33,6 @@ GameDatabase.confirmationTypes = [
name: "Dilation",
option: "dilation",
isUnlocked: () => PlayerProgress.realityUnlocked() || !Currency.tachyonParticles.eq(0),
}, {
name: "Reality",
option: "reality",
isUnlocked: () => PlayerProgress.realityUnlocked(),
}, {
name: "Reset Reality",
option: "resetReality",

View File

@ -143,7 +143,7 @@ GameDatabase.eternity.dilation = (function() {
initialCost: 1e14,
increment: 100,
pelleOnly: true,
description: () => `Multiply Dilated Time gain by ${format(5)}`,
description: () => `${formatX(5)} Dilated Time gain`,
effect: bought => Decimal.pow(5, bought),
formatEffect: value => formatX(value, 2),
formatCost: value => format(value, 2),

View File

@ -6,8 +6,9 @@ GameDatabase.eternity.timeStudies.dilation = [
description: "Unlock Time Dilation",
cost: 5000,
requirement: () => {
const ttRequirement = Currency.timeTheorems.max.gte(TimeStudy.dilation.totalTimeTheoremRequirement);
if (Ra.has(RA_UNLOCKS.AUTO_DILATION_UNLOCK) &&
Currency.timeTheorems.max.gte(TimeStudy.dilation.totalTimeTheoremRequirement) &&
ttRequirement &&
!isInCelestialReality() && !Pelle.isDoomed
) {
return true;
@ -15,7 +16,6 @@ GameDatabase.eternity.timeStudies.dilation = [
const tsRequirement = [231, 232, 233, 234].some(id => TimeStudy(id).isBought);
if (Perk.bypassECDilation.isBought && !Pelle.isDoomed) return tsRequirement;
const ecRequirement = EternityChallenge(11).isFullyCompleted && EternityChallenge(12).isFullyCompleted;
const ttRequirement = Currency.timeTheorems.max.gte(TimeStudy.dilation.totalTimeTheoremRequirement);
return tsRequirement && ecRequirement && ttRequirement;
}
},

View File

@ -1559,6 +1559,8 @@ Galaxies. Replicanti- or Tachyon Galaxies can't be spent for purchasing those up
const tab = GameDatabase.h2p.tabs[i];
tab.id = i;
if (tab.alias === undefined) tab.alias = tab.name;
tab.searchTermsRelevance = {};
}
const searchIndex = {};
@ -1576,7 +1578,13 @@ Galaxies. Replicanti- or Tachyon Galaxies can't be spent for purchasing those up
const addWord = (word, tab) => {
const lowerCase = word.toLowerCase();
for (let i = 0; i < lowerCase.length; i++) {
addTerm(lowerCase.slice(0, i + 1), tab);
const term = lowerCase.slice(0, i + 1);
addTerm(term, tab);
if (tab.searchTermsRelevance[term] === undefined) {
tab.searchTermsRelevance[term] = ((i + 1) / lowerCase.length) ** 0.65;
} else {
tab.searchTermsRelevance[term] = Math.max(tab.searchTermsRelevance[term], ((i + 1) / lowerCase.length) ** 0.65);
}
}
};
@ -1599,21 +1607,121 @@ Galaxies. Replicanti- or Tachyon Galaxies can't be spent for purchasing those up
addPhrase(tab.alias, tab);
}
GameDatabase.h2p.search = query => {
if (query === "") return GameDatabase.h2p.tabs;
const words = query.toLowerCase().split(" ").filter(str => str !== "");
/* eslint-disable-next-line no-unused-vars*/
const searchTerms = words.map((_word, i, arr) => arr.slice(0, arr.length - i).join(" "));
const result = new Set();
for (const searchWord of searchTerms) {
const matches = searchIndex[searchWord];
if (matches === undefined) continue;
for (const match of matches) {
result.add(match);
const map2dToObject = function(arr, keyFun, valueFun) {
const out = {};
for (let idx1 = 0; idx1 < arr.length; idx1++) {
for (let idx2 = 0; idx2 < arr[idx1].length; idx2++) {
out[keyFun(arr[idx1][idx2], idx1, idx2)] = valueFun(arr[idx1][idx2], idx1, idx2);
}
}
const results = Array.from(result);
results.sort((a, b) => a.id - b.id);
return out;
};
// Very suboptimal code coming up. If anybody has a better solution, PLEASE, implement it.
const keyboardify = keybrd => map2dToObject(keybrd.split(",").map(str => str.split("")),
key => key, (_key, x, y) => ({ x, y }));
const qwerty = keyboardify(`1234567890,qwertyuiop,asdfghjkl,zxcvbnm`);
const qwertz = keyboardify(`1234567890,qwertzuiop,asdfghjkl,yxcvbnm`);
const azerty = keyboardify(`1234567890,azertyuiop,qsdfghjklm,wxcvbn`);
const dvorak = keyboardify(`1234567890,'<>pyfgcrl,aoeuidhtns,;qjkxbmwvz`);
const colemak = keyboardify(`1234567890,qwfpgjluy,arstdhneio,zxcvbkm`);
const workman = keyboardify(`1234567890,qdrwbjfup,ashtgyneoi,zxmcvkl`);
const qwprf = keyboardify(`1234567890,qwprfyukl,asdtghnioe,zxcvbjm`);
const keyboards = [qwerty, qwertz, azerty, dvorak, colemak, workman, qwprf];
const keyboardDist = function(a, b, keyboard) {
const aPos = keyboard[a], bPos = keyboard[b];
if (!aPos || !bPos) return 100;
return Math.max(Math.abs(aPos.x - bPos.x), Math.abs(aPos.y - bPos.y));
};
// I copied this code based on OSA distance off wikipedia, with a few added changes.
// The cost for "substitution" (third item of the first Math.min) is replaced from a static value
// to a function which roughly estimates how likely the user is to mispress the key based on its
// minimum distance from several common keyboard layouts.
// I have no idea how the actual "distance" calculation works but as long as it does don't touch it.
const howBadlyTypoedWithKeyboard = function(a, b, keyboard) {
// If they're the same, skip all calculations
if (a === b) return 0;
const aLen = a.length;
const bLen = b.length;
// If they're way too different, don't bother
if (Math.abs(aLen - bLen) > 3) return 100;
// 2d Array with dimensions aLen + 1 x bLen + 1
const d = new Array(aLen + 1).fill(0).map(() => new Array(bLen + 1).fill(0));
for (let i = 0; i <= aLen; i++) {
d[i][0] = i;
}
for (let i = 0; i <= bLen; i++) {
d[0][i] = i;
}
for (let i = 1; i <= aLen; i++) {
for (let j = 1; j <= bLen; j++) {
const distance = keyboardDist(a[i - 1], b[j - 1], keyboard);
const cost = distance === 0 ? 0 : 0.3 + distance * distance * 0.25;
d[i][j] = Math.min(
d[i - 1][j] + 0.55,
d[i][j - 1] + 0.7,
d[i - 1][j - 1] + cost
);
}
}
return d[aLen][bLen];
};
const howBadlyTypoed = function(a, b) {
// Arbitrarily large number
let minTypoed = 1e10;
for (const keyboard of keyboards) {
minTypoed = Math.min(minTypoed, howBadlyTypoedWithKeyboard(a, b, keyboard));
}
return minTypoed;
};
const specialChars = ["'", "\"", ",", "-", ".", "_"];
const replaceSpecialChars = function(str) {
let result = str;
for (const i of specialChars) {
result = result.replaceAll(i, "");
}
return result;
};
// There are a LOT of magic numbers in this code, mostly from arbitrary choices for "What number is large enough to
// act as a placeholder for 'basically not found'?"
// This will need some cleanup if possible.
GameDatabase.h2p.search = query => {
const truncatedQuery = replaceSpecialChars(query);
if (truncatedQuery === "") return GameDatabase.h2p.tabs.map(x => ({ tab: x, relevance: 1.5 }));
const searchTerms = truncatedQuery.toLowerCase().split(" ").filter(str => str !== "");
// A higher "Relevance" value actually means it's further away from the search, important to keep in mind
const relevances = Array.repeat(1e4, GameDatabase.h2p.tabs.length);
for (const searchWord of searchTerms) {
const minimumRequirement = Math.min(searchWord.length - 0.9, 3) * 0.5;
for (const searchIndexStr in searchIndex) {
const typoThreshold = howBadlyTypoed(replaceSpecialChars(searchIndexStr), searchWord);
if (typoThreshold < minimumRequirement) {
for (const tab of searchIndex[searchIndexStr]) {
const maxRelevance = tab.searchTermsRelevance[searchIndexStr];
const decrease = Math.max(maxRelevance * 1.6 - 0.9, 0);
relevances[tab.id] = Math.min(relevances[tab.id], Math.max(typoThreshold, 1 - maxRelevance) - decrease);
}
}
}
}
const results = GameDatabase.h2p.tabs.filter(x => relevances[x.id] < 0.9)
.map(x => ({ tab: x, relevance: relevances[x.id] }));
// Provide both the relevance and the tab itself
// Sort by id first, then push more relevant results to top.
results.sort((a, b) => a.tab.id - b.tab.id).sort((a, b) => a.relevance - b.relevance);
// Provide both the relevance and the tab itself
return results;
};
}());

View File

@ -29,6 +29,7 @@ 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";

View File

@ -2476,7 +2476,7 @@ GameDatabase.news = [
{
id: "l8",
text: "A new group for the standardisation of numbers have come forward with a novel new format involving emoji's.",
get unlocked() { return player.requirementChecks.permanent.cancerGalaxies > 0; }
get unlocked() { return player.requirementChecks.permanent.emojiGalaxies > 0; }
},
{
id: "l9",
@ -2592,7 +2592,7 @@ GameDatabase.news = [
{
id: "l22",
text: "Anti Emoji Movie a huge hit!",
get unlocked() { return player.requirementChecks.permanent.cancerGalaxies >= 5; }
get unlocked() { return player.requirementChecks.permanent.emojiGalaxies >= 5; }
},
{
id: "l23",
@ -2717,7 +2717,7 @@ GameDatabase.news = [
{
id: "l45",
text: "Anti Emoji Movie MMMCMXCIX is a major hit!",
get unlocked() { return player.requirementChecks.permanent.cancerGalaxies >= 3999; }
get unlocked() { return player.requirementChecks.permanent.emojiGalaxies >= 3999; }
},
{
id: "l46",

View File

@ -359,7 +359,7 @@ GameDatabase.reality.automator = {
for example 71, 72, and 73 before the Dimension Split Dilation upgrade is purchased.<br>
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, <i>11-62</i>) and the following aliases:<br>
<blockquote><b>antimatter, infinity, time, active, passive, idle</b></blockquote>
<blockquote><b>antimatter, infinity, time, active, passive, idle, light, dark</b></blockquote>
A variable name may be used in place of Time Study list, see <b>define</b>.`,
examples: [
"studies nowait 11,21,31",

View File

@ -47,7 +47,7 @@ GameDatabase.speedrunMilestones = [
name: "Break Infinity",
description: "Break Infinity for the first time",
checkRequirement: () => player.break,
checkEverokennt: GAME_EVENT.BREAK_INFINITY,
checkEvent: GAME_EVENT.BREAK_INFINITY,
},
{
id: 7,
@ -199,7 +199,7 @@ GameDatabase.speedrunMilestones = [
key: "completeFullGame",
name: "Game Completed!",
description: "Complete the entire game",
checkRequirement: () => true,
checkEvent: GAME_EVENT.ACHIEVEMENT_EVENT_OTHER,
checkRequirement: () => Achievement(188).isUnlocked,
checkEvent: GAME_EVENT.ACHIEVEMENT_UNLOCKED,
},
];

View File

@ -475,7 +475,7 @@ GameDatabase.tabs = [
key: "shop",
name: "Shop",
symbol: "$",
component: "shop-tab",
component: "ShopTab",
id: 0,
hidable: true
}

View File

@ -1292,6 +1292,25 @@ GameStorage.devMigrations = {
delete player.celestials.laitela.darkAutobuyerTimer;
delete player.celestials.laitela.autoAnnihilationSetting;
},
GameStorage.migrations.etercreqConversion,
player => {
delete player.options.confirmations.reality;
},
player => {
const hasDimboost = player.celestials.pelle.upgrades.has(19);
const hasDilUpg = player.celestials.pelle.upgrades.has(18);
player.celestials.pelle.upgrades.delete(18);
player.celestials.pelle.upgrades.delete(19);
if (hasDimboost) player.celestials.pelle.upgrades.add(18);
if (hasDilUpg) player.celestials.pelle.upgrades.add(19);
},
player => {
delete player.auto.bulkOn;
},
player => {
player.requirementChecks.permanent.emojiGalaxies = player.requirementChecks.permanent.cancerGalaxies;
delete player.requirementChecks.permanent.cancerGalaxies;
}
],
patch(player) {

View File

@ -147,6 +147,7 @@ GameStorage.migrations = {
GameStorage.migrations.deleteFloatingTextOption(player);
GameStorage.migrations.refactorDoubleIPRebuyable(player);
GameStorage.migrations.convertNews(player);
GameStorage.migrations.etercreqConversion(player);
kong.migratePurchases();
}
@ -364,6 +365,8 @@ GameStorage.migrations = {
player.challenge.eternity.unlocked !== 0
) player.requirementChecks.reality.noPurchasedTT = false;
if (player.sacrificed.gt(0)) player.requirementChecks.infinity.noSacrifice = false;
player.requirementChecks.permanent.emojiGalaxies = player.spreadingCancer;
delete player.spreadingCancer;
},
adjustThemes(player) {
@ -815,7 +818,6 @@ GameStorage.migrations = {
},
migrateAutobuyers(player) {
player.auto.bulkOn = player.options.bulkOn;
player.auto.autobuyerOn = player.options.autobuyerOn;
delete player.options.bulkOn;
@ -901,6 +903,12 @@ GameStorage.migrations = {
delete player.infMult;
},
etercreqConversion(player) {
// eslint-disable-next-line no-bitwise
if (player.etercreq) player.challenge.eternity.requirementBits |= 1 << player.etercreq;
delete player.etercreq;
},
prePatch(saveData) {
// Initialize all possibly undefined properties that were not present in
// previous versions and which could be overwritten by deepmerge

View File

@ -33,7 +33,8 @@ export class ECTimeStudyState extends TimeStudyState {
if (!auto) {
Tab.challenges.eternity.show();
}
player.etercreq = this.id;
// eslint-disable-next-line no-bitwise
player.challenge.eternity.requirementBits |= 1 << this.id;
Currency.timeTheorems.subtract(this.cost);
TimeStudyTree.commitToGameState([TimeStudy.eternityChallenge(this.id)]);
return true;
@ -47,15 +48,18 @@ export class ECTimeStudyState extends TimeStudyState {
171, 171, 171,
143, 42, 121,
111, 123, 151,
181, 212, 214
181, 181, 181
];
TimeStudyTree.commitToGameState(buyStudiesUntil(studiesToBuy[this.id]));
// For EC 11 and 12, we can't choose between light and dark, but we can buy the
// pair of row 21 things
// If the player shift clicks an EC study that is immediately buyable, we try to
// buy it first - in case buying studies up to that point renders it unaffordable.
this.purchase();
TimeStudyTree.commitToGameState(buyStudiesUntil(studiesToBuy[this.id], this.id));
// For EC 11 and 12, we can't choose between light and dark,
// but we can buy the 191/193
if (this.id === 11) {
TimeStudy(211).purchase();
TimeStudy(191).purchase();
} else if (this.id === 12) {
TimeStudy(213).purchase();
TimeStudy(193).purchase();
}
this.purchase();
}
@ -105,13 +109,18 @@ export class ECTimeStudyState extends TimeStudyState {
}
get isEntryGoalMet() {
if (player.etercreq === this.id) return true;
if (this.wasRequirementPreviouslyMet) return true;
if (this.config.secondary.forbiddenStudies) return true;
const current = this.requirementCurrent;
const total = this.requirementTotal;
return typeof current === "number" ? current >= total : current.gte(total);
}
get wasRequirementPreviouslyMet() {
// eslint-disable-next-line no-bitwise
return (player.challenge.eternity.requirementBits & (1 << this.id)) !== 0;
}
invalidateRequirement() {
this.cachedCurrentRequirement = undefined;
}

View File

@ -116,25 +116,34 @@ TimeStudy.boughtNormalTS = function() {
};
TimeStudy.preferredPaths = {
get dimensionPath() {
return {
path: player.timestudy.preferredPaths[0],
studies: player.timestudy.preferredPaths[0].reduce((acc, path) =>
acc.concat(NormalTimeStudies.paths[path]), [])
};
dimension: {
get path() {
return player.timestudy.preferredPaths[0];
},
set path(value) {
const options = [1, 2, 3];
player.timestudy.preferredPaths[0] = value.filter(id => options.includes(id));
},
get studies() {
return player.timestudy.preferredPaths[0].flatMap(path => NormalTimeStudies.paths[path]);
},
get usePriority() {
return this.path.length > 1 ||
TimeStudy(201).isBought ||
DilationUpgrade.timeStudySplit.isBought ||
PlayerProgress.realityUnlocked();
}
},
set dimensionPath(value) {
const options = [1, 2, 3];
player.timestudy.preferredPaths[0] = value.filter(id => options.includes(id));
},
get pacePath() {
return {
path: player.timestudy.preferredPaths[1],
studies: NormalTimeStudies.paths[player.timestudy.preferredPaths[1]]
};
},
set pacePath(value) {
const options = [4, 5, 6];
player.timestudy.preferredPaths[1] = options.includes(value) ? value : 0;
pace: {
get path() {
return player.timestudy.preferredPaths[1];
},
set path(value) {
const options = [4, 5, 6];
player.timestudy.preferredPaths[1] = options.includes(value) ? value : 0;
},
get studies() {
return NormalTimeStudies.paths[player.timestudy.preferredPaths[1]];
}
}
};

View File

@ -1,13 +1,15 @@
import { GameMechanicState } from "../game-mechanics/index.js";
// This is only ever called from manual player actions, which means we can immediately commit them to the game state
export function buyStudiesUntil(id, repeatFor201 = true) {
const studyArray = [];
// eslint-disable-next-line complexity
export function buyStudiesUntil(id, ec = -1) {
let studyArray = [];
const lastInPrevRow = Math.floor(id / 10) * 10 - 1;
const requestedPath = TimeStudy(id).path;
const currTree = GameCache.currentStudyTree.value;
// Makes an array [start, start+1, ... , end], empty if end < start
const range = (start, end) => [...Array(Math.clampMin(end - start + 1, 0)).keys()].map(i => i + start);
const ecHasRequirement = !Perk.studyECRequirement.isBought;
// If the player tries to buy a study which is immediately buyable, we try to buy it first in case buying other
// studies up to that point renders it unaffordable. Effectively the clicked study is higher priority than all others
@ -17,34 +19,43 @@ export function buyStudiesUntil(id, repeatFor201 = true) {
// split, then we're done and don't need to attempt to buy any more
studyArray.push(...range(11, Math.min(lastInPrevRow, 70)));
studyArray.push(id);
if (id < 71) return studyArray;
// 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)
// - If we can't buy any additional paths or have 3 paths available, we attempt to buy everything here. With less
// than 3 paths available, this only purchases the rest of any unfinished paths (continues onward)
// - 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
// preferred paths. With less than 3 paths available, this only purchases the rest of any unfinished paths
// (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)
if (id < 111) {
studyArray.push(...NormalTimeStudies.paths[requestedPath].filter(s => s <= id));
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.dimensionPath.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;
}
if (currTree.currDimPathCount === currTree.allowedDimPathCount || currTree.allowedDimPathCount === 3) {
studyArray.push(...TimeStudy.preferredPaths.dimensionPath.studies);
studyArray.push(...range(71, 120));
} else if (TimeStudy.preferredPaths.dimensionPath.path.length > 0) {
studyArray.push(...TimeStudy.preferredPaths.dimensionPath.studies);
if (ec === 11 && ecHasRequirement) {
studyArray.push(...NormalTimeStudies.paths[TIME_STUDY_PATH.ANTIMATTER_DIM].filter(s => (s <= id)));
} else if (ec === 12 && ecHasRequirement) {
studyArray.push(...NormalTimeStudies.paths[TIME_STUDY_PATH.TIME_DIM].filter(s => (s <= id)));
} else if (currTree.currDimPathCount === currTree.allowedDimPathCount || currTree.allowedDimPathCount === 3) {
studyArray.push(...TimeStudy.preferredPaths.dimension.studies);
studyArray.push(...range(71, 103));
} else if (TimeStudy.preferredPaths.dimension.path.length > 0) {
studyArray.push(...TimeStudy.preferredPaths.dimension.studies);
} else {
GameUI.notify.error("You haven't selected a preferred Dimension path!");
GameUI.notify.error("You haven't selected a preferred Dimension path.");
return studyArray;
}
@ -61,12 +72,14 @@ export function buyStudiesUntil(id, repeatFor201 = true) {
// - If we have a preferred path, we buy it all (continues onward)
// - If we don't have any pace paths at this point, there's no way to objectively choose one (stops buying)
// - Fallback case: we have more than one path and intentionally do nothing here (continues onward)
const pacePaths = currTree.pacePaths
.map(pathName => NormalTimeStudies.pathList.find(p => p.name === pathName).path);
if (id < 151) {
studyArray.push(...NormalTimeStudies.paths[TimeStudy(id).path].filter(s => s <= id));
studyArray.push(...NormalTimeStudies.paths[TimeStudy(id).path].filter(s => (s <= id)));
return studyArray;
}
const pacePaths = currTree.pacePaths
.map(pathName => NormalTimeStudies.pathList.find(p => p.name === pathName).path);
if (V.isFullyCompleted && !Pelle.isDoomed) {
const allPace = NormalTimeStudies.paths[TIME_STUDY_PATH.ACTIVE]
.concat(NormalTimeStudies.paths[TIME_STUDY_PATH.PASSIVE])
@ -74,23 +87,38 @@ export function buyStudiesUntil(id, repeatFor201 = true) {
studyArray.push(...allPace);
} else if (pacePaths.length === 1) {
studyArray.push(...NormalTimeStudies.paths[pacePaths[0]]);
} else if (TimeStudy.preferredPaths.pacePath.path !== 0) {
studyArray.push(...TimeStudy.preferredPaths.pacePath.studies);
} else if (TimeStudy.preferredPaths.pace.path !== 0) {
studyArray.push(...TimeStudy.preferredPaths.pace.studies);
} else if (pacePaths.length === 0) {
GameUI.notify.error("You haven't selected a preferred Pace path!");
GameUI.notify.error("You haven't selected a preferred Pace path.");
return studyArray;
}
// Buy up to 201, then if applicable we commit what we have to the game state and recursively call this function
// again in order to attempt to buy another path
// First we buy up to 201 so we can buy the second preferred path if needed
studyArray.push(...range(151, Math.min(id, 201)));
if (id >= 201 && repeatFor201) {
TimeStudyTree.commitToGameState(studyArray);
return buyStudiesUntil(id, false);
}
if (id < 201) return studyArray;
studyArray.push(...range(211, Math.min(id, 214)));
studyArray.push(id);
// If we want to buy EC11 or EC12 we don't want 201 unless we have the EC study requirement perk
if (!(ecHasRequirement && (ec === 11 || ec === 12))) {
// We need to commit what we have to the game state, because the check for priorityRequirement
// requires us knowing if we have actually purchased 201.
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.");
}
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
// row. Try to buy that study now:
studyArray.push(id);
}
// Don't bother buying any more studies at or below row 22 unless the player has fully finished V, in which case just
// brute-force all of them up to the specified study. This buys all pre-triads, then triads sequentially up to the id

View File

@ -57,7 +57,9 @@ export class TimeStudyTree {
if (input.trim() === "") {
return false;
}
return /^(,?(\d+(-\d+)?|antimatter|infinity|time|active|passive|idle))*(\|\d+)?$/iu.test(input);
let test = input.replaceAll(/ +/gu, "");
TimeStudyTree.sets.forEach((_, x) => test = test.replaceAll(new RegExp(`${x},?`, "gu"), ""));
return /^,?((\d{2,3}(-\d{2,3})?)\b,?)*(\|\d{0,2})?$/iu.test(test);
}
// Getter for all the studies in the current game state
@ -80,6 +82,46 @@ export class TimeStudyTree {
GameCache.currentStudyTree.invalidate();
}
static get sets() {
// Grouping of studies. The key followed by an array of the studies the key is a shorthand for.
return new Map([
["antimatter", [71, 81, 91, 101]],
["infinity", [72, 82, 92, 102]],
["time", [73, 83, 93, 103]],
["active", [121, 131, 141]],
["passive", [122, 132, 142]],
["idle", [123, 133, 143]],
["light", [221, 223, 225, 227, 231, 233]],
["dark", [222, 224, 226, 228, 232, 234]],
]);
}
static truncateInput(input) {
let internal = input.toLowerCase();
// Convert every name into the ids it is a shorthand for
this.sets.forEach((ids, name) => (internal = internal.replace(name, ids.join())));
return internal
.replace(/[|,]$/u, "")
.replaceAll(" ", "");
}
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(",", ", ");
}
// This reads off all the studies in the import string and splits them into invalid and valid study IDs. We hold on
// to invalid studies for additional information to present to the player
parseStudyImport(input) {
@ -119,33 +161,6 @@ export class TimeStudyTree {
return output;
}
static truncateInput(input) {
return input
.toLowerCase()
.replaceAll("antimatter", "71,81,91,101")
.replaceAll("infinity", "72,82,92,102")
.replaceAll("time", "73,83,93,103")
.replaceAll("active", "121,131,141")
.replaceAll("passive", "122,132,142")
.replaceAll("idle", "123,133,143")
.replace(/[|,]$/u, "")
.replaceAll(" ", "")
.trim();
}
static formatStudyList(input) {
return input
.toLowerCase()
.replaceAll("71,81,91,101", "antimatter")
.replaceAll("72,82,92,102", "infinity")
.replaceAll("73,83,93,103", "time")
.replaceAll("121,131,141", "active")
.replaceAll("122,132,142", "passive")
.replaceAll("123,133,143", "idle")
.replaceAll(",", ", ")
.replace(/ +/gu, " ");
}
studyRangeToArray(firstNumber, lastNumber) {
const studiesArray = [];
const first = this.checkTimeStudyNumber(firstNumber);

View File

@ -118,17 +118,20 @@ export function gainedInfinityPoints() {
}
function totalEPMult() {
return new Decimal(getAdjustedGlyphEffect("cursedEP"))
.times(ShopPurchase.EPPurchases.currentMult)
.timesEffectsOf(
EternityUpgrade.epMult,
TimeStudy(61),
TimeStudy(122),
TimeStudy(121),
TimeStudy(123),
RealityUpgrade(12),
GlyphEffect.epMult
);
const totalMult = new Decimal(NG.multiplier);
return Pelle.isDisabled("EPMults")
? totalMult.times(Pelle.specialGlyphEffect.time.timesEffectOf(PelleRifts.famine.milestones[2]))
: totalMult.times(getAdjustedGlyphEffect("cursedEP"))
.times(ShopPurchase.EPPurchases.currentMult)
.timesEffectsOf(
EternityUpgrade.epMult,
TimeStudy(61),
TimeStudy(122),
TimeStudy(121),
TimeStudy(123),
RealityUpgrade(12),
GlyphEffect.epMult
);
}
export function gainedEternityPoints() {
@ -136,14 +139,6 @@ export function gainedEternityPoints() {
let ep = DC.D5.pow(player.records.thisEternity.maxIP.plus(
gainedInfinityPoints()).log10() / (308 - PelleRifts.war.effectValue.toNumber()) - 0.7).times(totalEPMult());
ep = ep.times(NG.multiplier);
const pelleMults = Pelle.specialGlyphEffect.time.timesEffectOf(
PelleRifts.famine.milestones[2]
);
if (Pelle.isDisabled("EPMults")) return ep.dividedBy(totalEPMult()).times(pelleMults).pow(pow).floor();
if (Teresa.isRunning) {
ep = ep.pow(0.55);
} else if (V.isRunning) {
@ -155,17 +150,11 @@ export function gainedEternityPoints() {
ep = ep.pow(getSecondaryGlyphEffect("timeEP"));
}
ep = ep.pow(pow);
return ep.floor();
return ep.pow(pow).floor();
}
export function requiredIPForEP(epAmount) {
const pelleMults = Pelle.specialGlyphEffect.time.timesEffectOf(PelleRifts.famine.milestones[2]);
if (Pelle.isDoomed) return Decimal.pow10(308 * (Decimal.log(pelleMults.dividedBy(epAmount).reciprocal(), 5) + 0.7))
.clampMin(Number.MAX_VALUE);
return Decimal.pow10(308 * (Decimal.log(totalEPMult().dividedBy(epAmount).reciprocal(), 5) + 0.7))
return Decimal.pow10(308 * (Decimal.log(Decimal.divide(Math.pow(epAmount, 1 / NG.power), totalEPMult()), 5) + 0.7))
.clampMin(Number.MAX_VALUE);
}

15660
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@
},
"dependencies": {
"@antimatter-dimensions/notations": "^2.2.0",
"@fortawesome/fontawesome-free": "^6.1.1",
"break_infinity.js": "^1.3.0",
"chevrotain": "^4.8.1",
"codemirror": "^5.65.1",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 693 KiB

View File

@ -7,14 +7,13 @@
<meta name="Antimatter Dimensions" content="A game about huge numbers and watching them go up." >
<script type="text/javascript" src='https://cdn1.kongregate.com/javascripts/kongregate_api.js'></script>
<link href="https://fonts.googleapis.com/css?family=PT+Mono" rel="stylesheet">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="stylesheets/fontawesome/css/all.css">
<link rel="stylesheet" type="text/css" href="stylesheets/codemirror/codemirror.css">
<link rel="stylesheet" type="text/css" href="stylesheets/codemirror/show-hint.css">
<link rel="stylesheet" type="text/css" href="stylesheets/codemirror/lint.css">
<link rel="stylesheet" type="text/css" href="stylesheets/codemirror/panda-syntax.css">
<link rel="stylesheet" type="text/css" href="stylesheets/codemirror/liquibyte.css">
<link rel="stylesheet" type="text/css" href="stylesheets/vue-sfc-classes.css">
<link rel="stylesheet" type="text/css" href="stylesheets/components.css">
<link rel="stylesheet" type="text/css" href="stylesheets/ad-slider-component.css">
<link rel="stylesheet" type="text/css" href="stylesheets/glyphs.css">
<link rel="stylesheet" type="text/css" href="stylesheets/styles.css?3">

View File

@ -1,13 +0,0 @@
/*
This file is for styles that are part of Vue components.
If your component is huge; it may want its own css file.
Things more directly related to the game and its theming should also be separated if possible.
*/
.l-hover-menu__wrapper {
position: relative;
}
.fa-compress-arrows-alt {
cursor: pointer;
}

7831
public/stylesheets/fontawesome/css/all.css vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -208,6 +208,18 @@
color: var(--color-reality-light);
}
.l-glyph-inventory-management {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
}
.o-glyph-inventory-management-group {
width: 20rem;
margin-bottom: 1rem;
}
.c-auto-sac-type-tab__input {
width: 2.5em;
height: 1.5em;
@ -585,8 +597,9 @@
.c-glyph-set-save-container {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
margin: 1rem 7rem 0;
justify-content: center;
margin: 1rem auto 0;
max-width: 30rem;
}
.c-glyph-single-set-save {
@ -594,7 +607,7 @@
flex-wrap: wrap;
justify-content: space-around;
align-items: flex-end;
height: 7.5rem;
margin: 0 0 1.5rem;
}
.c-glyph-single-set-save-flexbox {
@ -610,7 +623,7 @@
}
.c-glyph-sets-save-name__input {
width: 20rem;
width: 16rem;
height: 1.5em;
font-size: 1.35rem;
background-color: black;
@ -634,7 +647,7 @@
color: var(--color-reality-light);
border: 0.2rem solid var(--color-reality);
border-radius: 0.5rem;
margin-bottom: 1rem;
margin: 0 0.4rem 1rem;
transition-duration: 0.2s;
cursor: pointer;
}
@ -977,7 +990,7 @@
}
.c-glyph-inventory-option {
width: auto;
width: 19rem;
height: auto;
padding: 0.5rem;
margin-bottom: 0.5rem;
@ -1018,11 +1031,11 @@
font-family: Typewriter;
color: var(--color-reality);
background: black;
width: 200%;
left: -50%;
width: 100%;
left: 0%;
line-height: 1.8rem;
padding: 0.3rem;
z-index: 2;
z-index: 3;
pointer-events: none;
position: absolute;
bottom: 100%;

View File

@ -134,6 +134,11 @@ body.t-s9 {
color: var(--color-reality)
}
.t-dark .o-sidebar-currency--reality,
.t-dark-metro .o-sidebar-currency--reality {
color: var(--color-reality)
}
.o-sidebar-currency--pelle {
color: var(--color-pelle--base)
}
@ -898,6 +903,10 @@ body.t-s9 {
transition-delay: 0s;
}
.l-information {
top: 7rem;
}
.s-base--metro .l-help-me {
border-radius: 0;
}

View File

@ -34,11 +34,6 @@
flex: 1 0 auto;
}
.l-old-ui__footer {
padding-bottom: 1rem;
padding-top: 2.5rem;
}
.l-old-ui-page {
display: flex;
flex-direction: column;

View File

@ -54,9 +54,6 @@ html {
--color-v--base: #ead584;
--color-pelle--base: crimson;
--color-pelle-secondary: #00bcd4;
--color-ra-base: #9575cd;
--color-ra-pet-teresa: #8596ea;
--color-ra-pet-effarig: #ea8585;
@ -65,6 +62,9 @@ html {
--color-laitela--base: white;
--color-laitela--accent: black;
--color-pelle--base: crimson;
--color-pelle-secondary: #00bcd4;
}
.t-metro #ui-container, /* csslint allow: empty-rules */
@ -254,7 +254,7 @@ button:focus {
/*#region TT shop*/
#TTbuttons {
.TTbuttons {
color: var(--color-text);
position: fixed;
bottom: 0;
@ -304,7 +304,7 @@ button:focus {
background: #652F2F;
}
#timetheorems {
.timetheorems {
font-size: 15px;
text-align: center;
flex: 1 0 auto;
@ -592,40 +592,6 @@ button:focus {
display: none;
}
.o-save-timer {
color: var(--color-text);
position: fixed;
left: 0;
bottom: 0;
background-color: var(--color-base);
white-space: nowrap;
border-top: 0.1rem solid var(--color-accent);
border-right: 0.1rem solid var(--color-accent);
cursor: pointer;
user-select: none;
padding: 0 .5rem;
z-index: 5;
}
.o-speedrun-status {
font-size: 1.2rem;
color: var(--color-text);
position: fixed;
right: 1rem;
bottom: 1rem;
background-color: var(--color-base);
white-space: nowrap;
border: 0.2rem solid var(--color-accent);
user-select: none;
padding: 0.8rem 2rem;
z-index: 5;
}
.o-speedrun-collapse {
cursor: pointer;
padding: 0.2rem;
}
.l-reality-button {
display: block;
width: 50%;
@ -1302,7 +1268,7 @@ screen and (max-width: 480px) {
.o-primary-btn--subtab-option {
border: 0.2rem solid var(--color-accent);
margin: 0 0.8rem;
margin: 0.4rem 0.8rem;
line-height: 0;
}
@ -1313,6 +1279,7 @@ screen and (max-width: 480px) {
.o-primary-btn--tickspeed {
width: 17rem;
font-size: 1.2rem;
margin-right: 0.4rem;
}
.o-primary-btn--buy-max {
@ -1343,13 +1310,13 @@ screen and (max-width: 480px) {
.o-primary-btn--dimboost {
font-size: 0.9rem;
width: 21rem;
height: 4rem;
height: 4.5rem;
}
.o-primary-btn--galaxy {
font-size: 0.9rem;
width: 21rem;
height: 4rem;
height: 4.5rem;
}
.o-primary-btn--new-dim {
@ -1943,7 +1910,7 @@ screen and (max-width: 480px) {
.c-game-header__infinity-points {
font-size: 1.2rem;
width: 21rem;
width: 22rem;
}
.c-game-header__tesseract-available {
@ -2005,7 +1972,7 @@ screen and (max-width: 480px) {
.c-game-header__eternity-points {
font-size: 1.2rem;
width: 21rem;
width: 22rem;
}
.c-game-header__ep-amount {
@ -2785,7 +2752,7 @@ screen and (max-width: 480px) {
.s-base--dark .o-achievement__tooltip {
border: 0.1rem solid var(--color-text);
background: #111111;
background-color: #111111;
}
.s-base--dark .o-achievement__tooltip:after {
@ -5165,56 +5132,6 @@ screen and (max-width: 480px) {
fill: white;
}
.c-blob-snowflake-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
overflow: visible;
pointer-events: none;
user-select: none;
cursor: default;
}
.c-blob-background-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -9999;
overflow: visible;
pointer-events: none;
user-select: none;
cursor: default;
}
.o-blob-snowflake {
fill: #fbc21b;
text-shadow: 0 0 5px #000,
0 0 5px #000,
0 0 5px #000;
opacity: 0.9;
overflow: visible;
pointer-events: none;
user-select: none;
cursor: default;
}
.o-blob-background {
fill: #fbc21b;
text-shadow: 0 0 5px #000,
0 0 5px #000,
0 0 5px #000;
opacity: 0.3;
overflow: visible;
pointer-events: none;
user-select: none;
cursor: default;
}
/*#endregion Dilation*/
/*#region Modals*/
@ -5257,7 +5174,12 @@ screen and (max-width: 480px) {
font-size: 1.4rem;
padding: 1rem;
transition-duration: 0.2s;
overflow: hidden;
}
.c-modal__title {
width: 50rem;
font-size: 1.6rem;
font-weight: bold;
}
.c-modal__close-btn {
@ -5272,6 +5194,113 @@ screen and (max-width: 480px) {
right: -0.5rem;
}
.l-modal-buttons {
display: flex;
flex-direction: row;
justify-content: center;
}
.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);
}
.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 {
border: 0.1rem solid #1b5e20;
top: -0.1rem;
right: -0.1rem;
}
.c-modal__confirmation-toggle--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%;
}
.s-base--metro .c-modal__confirmation-toggle__tooltip {
border-radius: 0;
border-width: 0.1rem;
}
.t-s6 .c-modal__confirmation-toggle__tooltip,
.t-s10 .c-modal__confirmation-toggle__tooltip {
border: 0.1rem solid #1b5e20;
}
.c-modal__confirmation-toggle__tooltip:after {
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.2s;
z-index: 0;
}
.c-modal__confirmation-toggle:hover .c-modal__confirmation-toggle__tooltip {
opacity: 1;
bottom: calc(100% + 0.8rem);
}
.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 {
border-top-width: 0.6rem;
margin-bottom: -0.8rem;
}
.s-base--metro .c-modal__confirmation-toggle:hover .c-modal__confirmation-toggle__tooltip:after {
margin-bottom: -0.7rem;
}
.c-modal__confirm-btn {
background-color: var(--color-good) !important;
}
@ -5390,11 +5419,14 @@ screen and (max-width: 480px) {
flex-direction: column;
align-items: center;
min-width: 40rem;
}
.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 {
@ -5408,6 +5440,7 @@ screen and (max-width: 480px) {
margin-bottom: 0.2rem;
padding-bottom: 0.2rem;
border-bottom: 0.1rem solid var(--color-text);
cursor: pointer;
}
.c-modal-away-progress__resources div:last-child {
@ -5449,6 +5482,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)
}
.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
}
.c-modal-away-progress__realities,
@ -5494,6 +5534,20 @@ screen and (max-width: 480px) {
color: var(--color-ra-pet-v);
}
.c-modal-away-progress__teresa-memories,
.c-modal-away-progress__effarig-memories,
.c-modal-away-progress__enslaved-memories,
.c-modal-away-progress__v-memories {
filter: brightness(0.8);
}
.t-dark .c-modal-away-progress__teresa-memories,
.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
}
.c-modal-away-progress__black-hole b,
.c-modal-away-progress__black-hole {
color: black;
@ -5502,6 +5556,10 @@ screen and (max-width: 480px) {
0 0 0.3rem #e67919;
}
.c-modal-away-progress__reality-shards {
color: var(--color-pelle--base);
}
.c-modal-away-progress__disabled b,
.c-modal-away-progress__disabled {
text-decoration: line-through;
@ -5720,63 +5778,12 @@ kbd {
/*#endregion c-modal-hotkeys*/
.l-modal-progress-bar {
position: fixed;
z-index: 3;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
width: 40rem;
left: 50vw;
top: 50vh;
transform: translate(-50%, -50%);
}
.l-modal-progress-bar__hbox {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.l-modal-split-preferences {
display: flex;
flex-direction: row;
align-items: center;
}
.c-modal-progress-bar__label {
font-size: large;
padding-bottom: 0.5rem;
}
.l-modal-progress-bar__buttons {
display: flex;
flex-direction: row;
width: 25rem;
justify-content: space-between;
align-items: center;
}
.l-modal-progress-bar__bg {
width: 20rem;
margin-left: 1rem;
margin-right: 1rem;
height: 2rem;
}
.c-modal-progress-bar__bg {
background: black;
}
.l-modal-progress-bar__fg {
height: 100%;
}
.c-modal-progress-bar__fg {
background: blue;
}
.l-modal-celestial-quote {
position: fixed;
z-index: 3;
@ -7765,7 +7772,7 @@ kbd {
}
.o-alchemy-node--locked {
opacity: 0.1;
opacity: 0.25;
z-index: 4;
}
.o-alchemy-node--base {
@ -9076,7 +9083,7 @@ input.o-automator-block-input {
display: flex;
justify-content: flex-start;
flex-direction: column;
overflow-y: scroll;
overflow-y: auto;
margin: 0.5rem;
flex: 1 0.8 40rem;
}
@ -9125,7 +9132,7 @@ input.o-automator-block-input {
position: absolute;
top: 1rem;
width: 2rem;
right: 1rem;
right: 2rem;
height: 2rem;
border-radius: 50%;
line-height: 1.9rem;
@ -9134,6 +9141,10 @@ input.o-automator-block-input {
font-weight: 500;
}
.l-information {
top: 3.1rem;
}
.l-hide-modal-tab-container {
margin: 0.5rem;
}
@ -9366,96 +9377,6 @@ input.o-automator-block-input {
/* background: #1a1a1a; */
}
/* STD-shop */
#shop {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.c-shop-disclaimer {
color: black;
background: var(--color-bad);
width: 100rem;
font-size: 1.8rem;
font-weight: bold;
border: 0.2rem solid black;
border-radius: 1rem;
margin-top: 0.8rem;
}
.s-base--metro .c-shop-disclaimer {
border-width: 0.1rem;
border-radius: 0;
}
.t-s1 .c-shop-disclaimer,
.t-s6 .c-shop-disclaimer,
.t-s10 .c-shop-disclaimer {
color: var(--color-bad);
background: black;
border-color: var(--color-bad);
}
.c-shop-header {
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
margin: 1rem 0;
}
.c-shop-header img {
margin: 0 1rem;
}
.l-shop-buttons-container {
display: flex;
flex-wrap: wrap;
width: 62rem;
margin: auto;
}
.c-shop-button-container {
width: 30rem;
padding: 1rem;
border: .2rem solid #1f7d1f;
border-radius: .5rem;
margin: .5rem;
background: #3c3c3c;
color: white;
}
.o-shop-button-button {
background: turquoise;
border: none;
border-radius: .5rem;
display: flex;
margin: auto;
align-items: center;
font-family: Typewriter;
padding: .5rem 2rem;
margin-top: 1rem;
cursor: pointer;
}
.c-shop-header .o-shop-button-button {
margin: 0;
}
.o-shop-button-button img {
margin-left: 1rem;
}
.o-shop-button-multiplier {
font-weight: bold;
font-size: 1.5rem;
margin: 0.5rem 0;
display: block;
}
.c-autobuyer-buy-box {
display: flex;
justify-content: center;

View File

@ -8,7 +8,6 @@ body.t-inverted-metro {
.t-inverted-metro .c-modal,
.t-inverted-metro #TTbuttons,
.t-inverted-metro .sidebar,
.t-inverted-metro .o-save-timer,
.t-inverted-metro .c-glyph-tooltip {
filter: invert(100%);
}

View File

@ -8,7 +8,6 @@ body.t-inverted {
.t-inverted .c-modal,
.t-inverted #TTbuttons,
.t-inverted .sidebar,
.t-inverted .o-save-timer,
.t-inverted .c-glyph-tooltip {
filter: invert(100%);
}

View File

@ -19,7 +19,6 @@ body {
.t-s2 .c-modal,
.t-s2 #TTbuttons,
.t-s2 .sidebar,
.t-s2 .o-save-timer,
.t-s2 .c-glyph-tooltip {
filter: sepia(100%) hue-rotate(180deg) saturate(250%);
}

View File

@ -8,7 +8,6 @@ body {
.t-s3 .c-modal,
.t-s3 #TTbuttons,
.t-s3 .sidebar,
.t-s3 .o-save-timer,
.t-s3 .c-glyph-tooltip {
animation: glasses 7s infinite;
}

View File

@ -16,7 +16,6 @@ body.t-s5 {
.t-s5 .l-notification-container,
.t-s5 .c-modal,
.t-s5 #TTbuttons,
.t-s5 .sidebar,
.t-inverted-metro .o-save-timer {
.t-s5 .sidebar {
filter: sepia(100%) hue-rotate(0deg) saturate(100%);
}

View File

@ -1,14 +1,5 @@
/* Temporary inlined css classes from SFCs to prevent conflicts with old classes from styles.css */
/* FooterLinks.vue */
.o-footer {
margin-left: auto;
margin-right: auto;
text-align: center;
font-size: 1.5rem;
}
/* HintText.vue */
.o-hint-text {

View File

@ -1,6 +1,11 @@
import "./blob-snowflakes";
<script>
import BlobSnowflakes from "@/components/BlobSnowflakes";
Vue.component("background-animations", {
export default {
name: "BackgroundAnimations",
components: {
BlobSnowflakes,
},
data() {
return {
animateBackground: false,
@ -13,6 +18,9 @@ Vue.component("background-animations", {
this.blob = player.options.theme === "S11";
}
},
template: `
<blob-snowflakes v-if="blob" />`
});
};
</script>
<template>
<BlobSnowflakes v-if="blob" />
</template>

View File

@ -1,6 +1,11 @@
Vue.component("blob-background", {
<script>
export default {
name: "BlobBackground",
props: {
bounds: Object
bounds: {
type: Object,
required: true,
}
},
mounted() {
this.drop();
@ -31,7 +36,23 @@ Vue.component("blob-background", {
}
},
},
};
</script>
template:
`<text class="o-blob-background"></text>`
});
<template>
<text class="o-blob-background" />
</template>
<style scoped>
.o-blob-background {
fill: #fbc21b;
text-shadow: 0 0 5px #000,
0 0 5px #000,
0 0 5px #000;
opacity: 0.3;
overflow: visible;
pointer-events: none;
user-select: none;
cursor: default;
}
</style>

View File

@ -1,8 +1,13 @@
<script>
import TWEEN from "tween.js";
Vue.component("blob-snowflake", {
export default {
name: "BlobSnowflake",
props: {
bounds: Object
bounds: {
type: Object,
required: true,
}
},
mounted() {
this.fly();
@ -82,7 +87,24 @@ Vue.component("blob-snowflake", {
return SNOW[Math.floor(Math.random() * SNOW.length)];
}
},
},
template:
`<text class="o-blob-snowflake"></text>`
});
}
};
</script>
<template>
<text class="o-blob-snowflake" />
</template>
<style scoped>
.o-blob-snowflake {
fill: #fbc21b;
text-shadow: 0 0 5px #000,
0 0 5px #000,
0 0 5px #000;
opacity: 0.9;
overflow: visible;
pointer-events: none;
user-select: none;
cursor: default;
}
</style>

View File

@ -0,0 +1,94 @@
<script>
import BlobSnowflake from "@/components/BlobSnowflake";
import BlobBackground from "@/components/BlobBackground";
export default {
name: "BlobSnowflakes",
components: {
BlobSnowflake,
BlobBackground,
},
data() {
return {
animateBackground: false,
count: 0,
initialized: false,
bounds: {
x: 0,
y: 0
}
};
},
mounted() {
this.updateSize();
window.addEventListener("resize", this.updateSize);
this.initialized = true;
},
destroyed() {
window.removeEventListener("resize", this.updateSize);
},
methods: {
update() {
this.animateBackground = player.options.animations.background;
this.count = player.options.animations.blobSnowflakes;
},
updateSize() {
this.bounds.x = document.getElementById("ui").clientWidth;
this.bounds.y = document.getElementById("ui").clientHeight;
}
},
};
</script>
<template>
<div v-if="initialized">
<svg
v-if="animateBackground"
class="c-blob-snowflake-container"
>
<BlobSnowflake
v-for="i in count"
:key="i"
:bounds="bounds"
/>
</svg>
<svg
v-else
class="c-blob-background-container"
>
<BlobBackground
v-for="i in count"
:key="i"
:bounds="bounds"
/>
</svg>
</div>
</template>
<style scoped>
.c-blob-snowflake-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
overflow: visible;
pointer-events: none;
user-select: none;
cursor: default;
}
.c-blob-background-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -9999;
overflow: visible;
pointer-events: none;
user-select: none;
cursor: default;
}
</style>

View File

@ -76,7 +76,7 @@ export default {
if (this.downButtonEnabled) this.lastVisibleIndex++;
},
removeQuoteSyntax(x) {
return Modal.celestialQuote.removeOverrideCel(x).replace("*", "");
return Modal.celestialQuote.removeOverrideCel(x);
}
}
};

View File

@ -1,5 +1,4 @@
<script>
import { isDecimal, isFunction, isNumber } from "@/utility";
/* eslint-disable no-empty-function */

View File

@ -1,11 +0,0 @@
<script>
export default {
name: "FooterLink"
};
</script>
<template>
<a target="_newtab">
<slot />
</a>
</template>

View File

@ -1,45 +0,0 @@
<script>
import FooterLink from "@/components/FooterLink";
export default {
name: "FooterLinks",
components: {
FooterLink
},
computed: {
isVisible() {
return this.$viewModel.subtab !== Tab.eternity.studies.key;
}
}
};
</script>
<template>
<div
v-if="isVisible"
class="o-footer"
>
<FooterLink href="https://example.com">
Example
</FooterLink>
|
<FooterLink
href="about.html"
onclick="SecretAchievement(33).unlock()"
>
Donate
</FooterLink>
|
<FooterLink href="changelog.html">
Changelog
</FooterLink>
|
<FooterLink href="https://discord.gg/ST9NaXa">
Discord
</FooterLink>
|
<FooterLink href="https://www.reddit.com/r/AntimatterDimensions/">
Subreddit
</FooterLink>
</div>
</template>

View File

@ -0,0 +1,109 @@
<script>
import ClassicUi from "@/components/ui-modes/classic/ClassicUi";
import ModernUi from "@/components/ui-modes/modern/ModernUi";
import ModernSidebar from "@/components/ui-modes/modern/ModernSidebar";
import TabComponents from "@/components/tabs";
import PopupModal from "@/components/modals/PopupModal";
import FadeToBlack from "@/components/tabs/celestial-pelle/FadeToBlack";
import CreditsContainer from "@/components/tabs/celestial-pelle/CreditsContainer";
import NewGame from "@/components/tabs/celestial-pelle/NewGame";
import SaveTimer from "@/components/SaveTimer";
import SpeedrunStatus from "@/components/SpeedrunStatus";
import BackgroundAnimations from "@/components/BackgroundAnimations";
import ModalProgressBar from "@/components/modals/ModalProgressBar";
import HowToPlay from "@/components/HowToPlay";
import InfoButton from "@/components/InfoButton";
import TimeTheoremShop from "@/components/tabs/time-studies/tt-shop/TimeTheoremShop";
export default {
name: "GameUIComponent",
components: {
...TabComponents,
ClassicUi,
ModernUi,
ModernSidebar,
PopupModal,
FadeToBlack,
CreditsContainer,
NewGame,
SaveTimer,
SpeedrunStatus,
BackgroundAnimations,
ModalProgressBar,
HowToPlay,
InfoButton,
TimeTheoremShop
},
computed: {
view() {
return this.$viewModel;
},
uiLayout() {
return this.view.newUI ? "ModernUi" : "ClassicUi";
},
containerClass() {
return this.view.newUI ? "new-ui" : "old-ui";
},
page() {
const subtab = Tabs.current[this.$viewModel.subtab];
return subtab.config.component;
},
themeCss() {
return `stylesheets/theme-${this.view.theme}.css`;
}
},
};
</script>
<template>
<div
v-if="view.initialized"
id="ui-container"
:class="containerClass"
class="ui-wrapper"
>
<div
id="ui"
class="c-game-ui"
>
<component :is="uiLayout">
<component :is="page" />
</component>
<PopupModal
v-if="view.modal.current"
:modal="view.modal.current"
/>
<ModalProgressBar v-if="view.modal.progressBar" />
<link
v-if="view.theme !== 'Normal'"
type="text/css"
rel="stylesheet"
:href="themeCss"
>
<HowToPlay />
<InfoButton />
<BackgroundAnimations />
</div>
<div
id="notification-container"
class="l-notification-container"
/>
<TimeTheoremShop
v-if="view.subtab === 'studies'"
class="l-time-studies-tab__tt-shop"
/>
<ModernSidebar v-if="view.newUI" />
<SaveTimer />
<SpeedrunStatus />
<FadeToBlack />
<CreditsContainer />
<NewGame />
</div>
</template>
<style scoped>
.ui-wrapper {
display: flex;
justify-content: center;
}
</style>

View File

@ -106,7 +106,7 @@ export default {
<GlyphComponent
v-for="(g, idx) in glyphs"
:key="idx"
style="margin: 0.2rem;"
class="l-preview"
:glyph="g"
:show-sacrifice="showSacrifice"
:draggable="false"
@ -127,3 +127,9 @@ export default {
</span>
</div>
</template>
<style scoped>
.l-preview {
margin: 0.2rem;
}
</style>

23
src/components/HelpMe.vue Normal file
View File

@ -0,0 +1,23 @@
<script>
export default {
name: "HelpMe",
methods: {
showModal() {
Modal.h2p.show();
}
}
};
</script>
<template>
<div
class="o-tab-btn l-help-me"
@click="showModal"
>
?
</div>
</template>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<script>
export default {
name: "HowToPlay",
methods: {
showModal() {
Modal.h2p.show();
}
}
};
</script>
<template>
<div
class="o-tab-btn l-help-me"
@click="showModal"
>
?
</div>
</template>
<style scoped>
</style>

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