Merge branch 'master' into Steam-Webpack

This commit is contained in:
ZackRhodes 2022-12-16 23:54:14 -05:00
commit 67a278147a
34 changed files with 566 additions and 121 deletions

View File

@ -20,6 +20,10 @@ export class PlayerProgress {
return this._player.realities > 0;
}
get hasFullCompletion() {
return this._player.records?.fullGameCompletions > 0;
}
static get current() {
return new PlayerProgress(player);
}

View File

@ -26,7 +26,7 @@ class CosmeticGlyphType {
get defaultColor() {
const color = this.id === "reality" ? GlyphAppearanceHandler.realityColor : this._defaultColor;
const isNormallyDark = player.options.forceDarkGlyphs || Theme.current().isDark();
const isNormallyDark = !player.options.lightGlyphs;
return {
border: color,
bg: (isNormallyDark === (this.id === "cursed")) ? "white" : "black",
@ -154,19 +154,21 @@ export const GlyphAppearanceHandler = {
},
getBorderColor(type) {
if (type === "cursed" && !CosmeticGlyphTypes.cursed.currentColor.str)
return player.options.lightGlyphs ? "white" : "black";
return CosmeticGlyphTypes[type].currentColor.border;
},
getRarityColor(strength) {
const isDarkBG = player.options.forceDarkGlyphs || Theme.current().isDark();
const isDarkBG = !player.options.lightGlyphs;
return getRarity(strength)[isDarkBG ? "darkColor" : "lightColor"];
},
getColorProps(colorStr) {
// This condition is a bit odd - this specifically selects out the hybrid custom colors which have both a BG color
// and a hex code. Reality color looks like "rgb(..." and also goes in this conditional
if (colorStr.charAt(1) !== "#") {
if (colorStr?.charAt(1) !== "#") {
return {
border: colorStr,
bg: (player.options.forceDarkGlyphs || Theme.current().isDark()) ? "black" : "white",
bg: player.options.lightGlyphs ? "white" : "black",
};
}
return {
@ -176,7 +178,7 @@ export const GlyphAppearanceHandler = {
},
// Only used to ensure readable glyph tooltips
getBaseColor(isInverted) {
const isNormallyDark = player.options.forceDarkGlyphs || Theme.current().isDark();
const isNormallyDark = !player.options.lightGlyphs;
if (isInverted) return isNormallyDark ? "white" : "black";
return isNormallyDark ? "black" : "white";
},

View File

@ -805,7 +805,7 @@ window.player = {
ignoreGlyphEffects: false,
ignoreGlyphLevel: false,
ignoreGlyphRarity: false,
forceDarkGlyphs: true,
lightGlyphs: false,
showHintText: {
achievements: true,
achievementUnlockStates: true,

View File

@ -68,7 +68,7 @@ GameDatabase.celestials.ra = {
effect: () => 1 + Math.pow(Currency.realityMachines.value.pLog10() / 100, 0.5),
pet: "teresa",
level: 5,
displayIcon: `Δ`
displayIcon: `Ϟ`
},
alteredGlyphs: {
id: 3,

View File

@ -11,8 +11,7 @@ GameDatabase.changelog = [
* }
*/
{
// TODO This is just a placeholder date (today), change this to the actual release date
date: [2022, 11, 21],
date: [2022, 12, 17],
name: "The Reality Update",
info: `
<b>MAJOR STUFF:</b><br>

View File

@ -5,13 +5,17 @@ import { PlayerProgress } from "../../app/player-progress";
import { MultiplierTabIcons } from "./icons";
// See index.js for documentation
// Note most of the isActive entries in here have redundant-looking DT/s != 0 checks because DT is treated as a
// special case due to not being a prestige currency but still needing to be treated like one in the UI. This
// is because it requires dilation to be unlocked, which isn't a given, and we want the tab continuously visible
// after the first ever dilation unlock on the 0th reality
GameDatabase.multiplierTabValues.DT = {
total: {
name: "Dilated Time gain",
isBase: true,
displayOverride: () => `${format(getDilationGainPerSecond().times(getGameSpeedupForDisplay()), 2, 2)}/sec`,
multValue: () => getDilationGainPerSecond().times(getGameSpeedupForDisplay()),
isActive: () => getDilationGainPerSecond().gt(0),
isActive: () => PlayerProgress.realityUnlocked() ||
(PlayerProgress.dilationUnlocked() && getDilationGainPerSecond().gt(0)),
dilationEffect: () => (Enslaved.isRunning ? 0.85 : 1),
isDilated: true,
overlay: ["Ψ"],
@ -19,7 +23,8 @@ GameDatabase.multiplierTabValues.DT = {
achievement: {
name: "Achievements",
multValue: () => Achievement(132).effectOrDefault(1) * Achievement(137).effectOrDefault(1),
isActive: () => Achievement(132).canBeApplied || Achievement(137).canBeApplied,
isActive: () => (Achievement(132).canBeApplied || Achievement(137).canBeApplied) &&
getDilationGainPerSecond().neq(0),
icon: MultiplierTabIcons.ACHIEVEMENT,
},
dilation: {
@ -39,21 +44,25 @@ GameDatabase.multiplierTabValues.DT = {
gamespeed: {
name: "Current Game speed",
multValue: () => getGameSpeedupForDisplay(),
isActive: () => getGameSpeedupForDisplay() > 1,
isActive: () => getGameSpeedupForDisplay() > 1 && getDilationGainPerSecond().neq(0),
icon: MultiplierTabIcons.GAMESPEED,
},
realityUpgrade: {
name: "Temporal Amplifier",
multValue: () => RealityUpgrade(1).effectOrDefault(1),
isActive: () => RealityUpgrade(1).canBeApplied && !Pelle.isDoomed,
isActive: () => RealityUpgrade(1).canBeApplied && getDilationGainPerSecond().neq(0) && !Pelle.isDoomed,
icon: MultiplierTabIcons.UPGRADE("reality"),
},
glyph: {
name: "Glyph Effects",
multValue: () => Decimal.times(getAdjustedGlyphEffect("dilationDT"),
Math.clampMin(Decimal.log10(Replicanti.amount) * getAdjustedGlyphEffect("replicationdtgain"), 1))
.times(Pelle.specialGlyphEffect.dilation),
isActive: () => PlayerProgress.realityUnlocked(),
multValue: () => {
const dtMult = getAdjustedGlyphEffect("dilationDT").times(Pelle.specialGlyphEffect.dilation);
const repliDT = Replicanti.areUnlocked
? Math.clampMin(Decimal.log10(Replicanti.amount) * getAdjustedGlyphEffect("replicationdtgain"), 1)
: DC.D1;
return dtMult.times(repliDT);
},
isActive: () => PlayerProgress.realityUnlocked() && getDilationGainPerSecond().neq(0),
icon: MultiplierTabIcons.GENERIC_GLYPH
},
ra: {
@ -62,32 +71,32 @@ GameDatabase.multiplierTabValues.DT = {
Ra.unlocks.continuousTTBoost.effects.dilatedTime,
Ra.unlocks.peakGamespeedDT
),
isActive: () => Ra.unlocks.autoTP.canBeApplied,
isActive: () => Ra.unlocks.autoTP.canBeApplied && getDilationGainPerSecond().neq(0),
icon: MultiplierTabIcons.GENERIC_RA,
},
alchemy: {
name: "Glyph Alchemy",
multValue: () => AlchemyResource.dilation.effectOrDefault(1),
isActive: () => Ra.unlocks.unlockGlyphAlchemy.canBeApplied,
isActive: () => Ra.unlocks.unlockGlyphAlchemy.canBeApplied && getDilationGainPerSecond().neq(0),
icon: MultiplierTabIcons.ALCHEMY,
},
iap: {
name: "Shop Tab Purchases",
multValue: () => new Decimal(ShopPurchase.dilatedTimePurchases.currentMult ** (Pelle.isDoomed ? 0.5 : 1)),
isActive: () => ShopPurchaseData.totalSTD > 0,
isActive: () => ShopPurchaseData.totalSTD > 0 && getDilationGainPerSecond().neq(0),
icon: MultiplierTabIcons.IAP,
},
nerfV: {
name: "V's Reality",
powValue: () => 0.5,
isActive: () => V.isRunning,
isActive: () => V.isRunning && getDilationGainPerSecond().neq(0),
icon: MultiplierTabIcons.GENERIC_V,
},
nerfPelle: {
name: "Doomed Nerfs",
multValue: 1e-5,
isActive: () => Pelle.isDoomed,
isActive: () => Pelle.isDoomed && getDilationGainPerSecond().neq(0),
icon: MultiplierTabIcons.PELLE,
}
};

View File

@ -9,8 +9,9 @@ GameDatabase.multiplierTabValues.EP = {
total: {
name: "Total EP Gained on Eternity",
isBase: true,
multValue: () => gainedEternityPoints(),
isActive: () => new Decimal(Currency.eternities.value).gt(0) || gainedEternityPoints().gt(0),
// This effectively hides everything if the player can't actually gain any
multValue: () => (Player.canEternity ? gainedEternityPoints() : 0),
isActive: () => PlayerProgress.eternityUnlocked() || Player.canEternity,
dilationEffect: () => (Laitela.isRunning ? 0.75 * Effects.product(DilationUpgrade.dilationPenalty) : 1),
isDilated: true,
overlay: ["Δ", "<i class='fa-solid fa-layer-group' />"],

View File

@ -7,7 +7,6 @@ import { MultiplierTabIcons } from "./icons";
GameDatabase.multiplierTabValues.gamespeed = {
total: {
name: "Game speed",
isBase: true,
displayOverride: () => {
if (Enslaved.isStoringRealTime) return `Set to ${format(0)} (storing real time)`;
if (EternityChallenge(12).isRunning) return `${formatX(1)}/${formatInt(1000)} (fixed)`;

View File

@ -9,7 +9,9 @@ GameDatabase.multiplierTabValues.infinities = {
name: "Infinities gained per Crunch",
isBase: true,
multValue: () => gainedInfinities(),
isActive: () => Achievement(87).isUnlocked && !EternityChallenge(4).isRunning && !Pelle.isDoomed,
// The earliest sources of infinity multipliers are ach87 and TS32, which may happen in either order
isActive: () => (Achievement(87).isUnlocked || PlayerProgress.eternityUnlocked()) &&
!EternityChallenge(4).isRunning && !Pelle.isDoomed,
overlay: ["∞", "<i class='fa-solid fa-arrows-rotate' />"],
},
achievement: {

View File

@ -23,8 +23,10 @@ GameDatabase.multiplierTabValues.ID = {
.filter(id => id.isProducing)
.map(id => id.multiplier)
.reduce((x, y) => x.times(y), DC.D1)),
isActive: dim => (InfinityDimension(dim ?? 1).isProducing || EternityMilestone.autoUnlockID.isReached) &&
!EternityChallenge(11).isRunning,
isActive: dim => !EternityChallenge(11).isRunning &&
(dim
? InfinityDimension(dim).isProducing
: (PlayerProgress.eternityUnlocked() || InfinityDimension(1).isProducing)),
dilationEffect: () => {
const baseEff = player.dilation.active
? 0.75 * Effects.product(DilationUpgrade.dilationPenalty)

View File

@ -9,8 +9,9 @@ GameDatabase.multiplierTabValues.IP = {
total: {
name: "Total IP Gained on Infinity",
isBase: true,
multValue: () => gainedInfinityPoints(),
isActive: () => new Decimal(Currency.infinities.value).gt(0) || gainedInfinityPoints().gt(0),
// This effectively hides everything if the player can't actually gain any
multValue: () => (Player.canCrunch ? gainedInfinityPoints() : 0),
isActive: () => PlayerProgress.infinityUnlocked() || Player.canCrunch,
dilationEffect: () => (Laitela.isRunning ? 0.75 * Effects.product(DilationUpgrade.dilationPenalty) : 1),
isDilated: true,
overlay: ["∞", "<i class='fa-solid fa-layer-group' />"],

View File

@ -14,9 +14,14 @@ GameDatabase.multiplierTabValues.TP = {
? `${baseTPStr}${formatPow(PelleRifts.paradox.milestones[1].effectValue, 1, 1)}`
: baseTPStr;
},
multValue: () => new Decimal(Currency.tachyonParticles.value)
.pow(PelleRifts.paradox.milestones[1].effectOrDefault(1)),
isActive: () => new Decimal(Currency.tachyonParticles.value).gt(0),
// This is treated as a multiplier and not a prestige currency, with an overridden display;
// therefore we need to return 1 "by default"
multValue: () => {
const baseTP = new Decimal(Currency.tachyonParticles.value)
.pow(PelleRifts.paradox.milestones[1].effectOrDefault(1));
return TimeStudy.dilation.isBought ? baseTP : 1;
},
isActive: () => PlayerProgress.realityUnlocked() || PlayerProgress.dilationUnlocked(),
icon: MultiplierTabIcons.TACHYON_PARTICLES,
},
base: {

View File

@ -23,7 +23,8 @@ GameDatabase.multiplierTabValues.TD = {
.filter(td => td.isProducing)
.map(td => td.multiplier)
.reduce((x, y) => x.times(y), DC.D1)),
isActive: dim => TimeDimension(dim ?? 1).isProducing && !EternityChallenge(11).isRunning,
isActive: dim => !EternityChallenge(11).isRunning &&
(dim ? TimeDimension(dim).isProducing : (PlayerProgress.realityUnlocked() || TimeDimension(1).isProducing)),
dilationEffect: () => {
const baseEff = player.dilation.active
? 0.75 * Effects.product(DilationUpgrade.dilationPenalty)

View File

@ -1530,6 +1530,10 @@ GameStorage.devMigrations = {
if (glyph.type === "companion") glyph.color = undefined;
}
},
player => {
player.options.lightGlyphs = !player.options.forceDarkGlyphs;
delete player.options.forceDarkGlyphs;
}
],
patch(player) {

View File

@ -233,7 +233,7 @@ export const GameStorage = {
// Needed to check some notification about reality unlock study.
EventHub.dispatch(GAME_EVENT.SAVE_CONVERTED_FROM_PREVIOUS_VERSION);
}
if (DEV) {
if (DEV || player.options.testVersion !== undefined) {
this.devMigrations.patch(player);
}
}

View File

@ -57,7 +57,7 @@
color: white;
background-color: gray;
border-radius: var(--var-border-radius, 0.2rem);
padding: 0.2rem;
padding: 0.1rem;
}
.c-glyph-component--dragging {
@ -1378,62 +1378,32 @@
@keyframes a-reality-glyph-name-cycle {
0% {
color: #b67f33;
text-shadow:
var(--color-text-inverted) -1px 1px 1px,
var(--color-text-inverted) 1px 1px 1px,
var(--color-text-inverted) -1px -1px 1px,
var(--color-text-inverted) 1px -1px 1px,
#b67f33 0 0 3px;
text-shadow: #b67f33 0 0 3px;
}
20% {
color: #64dd17;
text-shadow:
var(--color-text-inverted) -1px 1px 1px,
var(--color-text-inverted) 1px 1px 1px,
var(--color-text-inverted) -1px -1px 1px,
var(--color-text-inverted) 1px -1px 1px,
#64dd17 0 0 3px;
text-shadow: #64dd17 0 0 3px;
}
40% {
color: #22aa48;
text-shadow:
var(--color-text-inverted) -1px 1px 1px,
var(--color-text-inverted) 1px 1px 1px,
var(--color-text-inverted) -1px -1px 1px,
var(--color-text-inverted) 1px -1px 1px,
#22aa48 0 0 3px;
text-shadow: #22aa48 0 0 3px;
}
60% {
color: #03a9f4;
text-shadow:
var(--color-text-inverted) -1px 1px 1px,
var(--color-text-inverted) 1px 1px 1px,
var(--color-text-inverted) -1px -1px 1px,
var(--color-text-inverted) 1px -1px 1px,
#03a9f4 0 0 3px;
text-shadow: #03a9f4 0 0 3px;
}
80% {
color: #b241e3;
text-shadow:
var(--color-text-inverted) -1px 1px 1px,
var(--color-text-inverted) 1px 1px 1px,
var(--color-text-inverted) -1px -1px 1px,
var(--color-text-inverted) 1px -1px 1px,
#b241e3 0 0 3px;
text-shadow: #b241e3 0 0 3px;
}
100% {
color: #b67f33;
text-shadow:
var(--color-text-inverted) -1px 1px 1px,
var(--color-text-inverted) 1px 1px 1px,
var(--color-text-inverted) -1px -1px 1px,
var(--color-text-inverted) 1px -1px 1px,
#b67f33 0 0 3px;
text-shadow: #b67f33 0 0 3px;
}
}

View File

@ -748,7 +748,7 @@ button:focus {
height: 6rem;
}
.s-base--metro .l-reality-button {
.t-metro .l-reality-button {
box-shadow: 0.1rem 0.1rem 0.1rem 0 #9e9e9e;
}
@ -1453,21 +1453,26 @@ br {
visibility: hidden;
white-space: nowrap;
position: absolute;
left: 105%;
left: 100%;
z-index: 2;
text-align: left;
opacity: 0;
color: var(--color-text);
background-color: var(--color-background);
border: 0.3rem solid var(--color-text);
border: var(--var-border-width, 0.2rem) solid var(--color-text);
border-radius: var(--var-border-radius, 0.5rem);
padding: 0.5rem;
transition: opacity 0.3s, visibility 0.3s;
margin-top: -0.4rem;
transition-duration: 0.3s;
pointer-events: none;
}
.infotooltip:hover .infotooltiptext {
left: calc(100% + 0.7rem);
}
.s-base--dark .infotooltip .infotooltiptext {
border: 0.1rem solid #dddddd;
border-color: #dddddd;
}
.infotooltip:hover .infotooltiptext {
@ -1478,11 +1483,18 @@ br {
.infotooltip .infotooltiptext::after {
content: "";
position: absolute;
top: 50%;
top: 2.8rem;
right: 100%;
border: 0.5rem solid transparent;
border-right-color: #0d0d0d;
border-right: 0 solid black;
border-top: 0.6rem solid transparent;
border-bottom: 0.6rem solid transparent;
transform: translateY(-50%);
margin-right: 0.1rem;
transition-duration: 0.3s;
}
.infotooltip:hover .infotooltiptext::after {
border-right-width: 0.6rem;
}
.s-base--dark .infotooltip .infotooltiptext::after {
@ -2003,11 +2015,7 @@ br {
color: black;
}
.s-base--metro .o-infinity-button {
box-shadow: 0.1rem 0.1rem 0.1rem 0 #9e9e9e;
}
.t-dark-metro .o-infinity-button {
.t-metro .o-infinity-button {
box-shadow: 0.1rem 0.1rem 0.1rem 0 #9e9e9e;
}
@ -2048,7 +2056,7 @@ br {
border-color: black;
}
.s-base--metro .o-eternity-button {
.t-metro .o-eternity-button {
box-shadow: 0.1rem 0.1rem 0.1rem 0 #9e9e9e;
}
@ -5629,6 +5637,7 @@ properly on certain themes. */
border-bottom: 0.1rem solid var(--color-text);
margin-bottom: 0.2rem;
padding-bottom: 0.2rem;
min-width: 55rem;
cursor: pointer;
}
@ -9198,6 +9207,7 @@ input.o-automator-block-input {
/* stylelint-disable-next-line unit-allowed-list */
height: calc(100vh - 20vh);
margin: 0.5rem;
overflow: hidden;
}
.l-h2p-container {

View File

@ -140,13 +140,23 @@ export default {
colorObj() {
let overrideColor;
if (this.glyph.color) overrideColor = GlyphAppearanceHandler.getColorProps(this.glyph.color);
if (this.glyph.cosmetic) overrideColor = this.cosmeticConfig.currentColor;
if (this.glyph.cosmetic) {
if (this.glyph.cosmetic === this.glyph.type) {
overrideColor = this.glyph.type === "cursed"
? GlyphAppearanceHandler.getBaseColor(true)
: this.cosmeticConfig.currentColor;
} else {
overrideColor = this.cosmeticConfig.currentColor;
}
}
let symbolColor;
if (this.isRealityGlyph) symbolColor = this.realityColor;
else symbolColor = this.cosmeticConfig.ignoreRarityColor
? GlyphAppearanceHandler.getBorderColor(this.glyph.type)
: GlyphAppearanceHandler.getRarityColor(this.glyph.strength);
else {
symbolColor = this.cosmeticConfig.ignoreRarityColor
? GlyphAppearanceHandler.getBorderColor(this.glyph.type)
: GlyphAppearanceHandler.getRarityColor(this.glyph.strength);
}
return {
border: overrideColor?.border ?? GlyphAppearanceHandler.getBorderColor(this.glyph.type),

View File

@ -101,6 +101,10 @@ export default {
closeEvent: GAME_EVENT.GLYPH_SET_SAVE_CHANGE,
displaySacrifice: this.showSacrifice,
});
},
// Necessary to force a re-render for the set name if the set itself changes
glyphHash() {
return Glyphs.hash(this.glyphs);
}
}
};
@ -119,6 +123,7 @@ export default {
>
<GlyphSetName
v-if="showName"
:key="glyphHash()"
:glyph-set="glyphs"
:force-color="forceNameColor"
/>

View File

@ -98,8 +98,9 @@ export default {
},
descriptionStyle() {
const color = GlyphAppearanceHandler.getRarityColor(this.strength);
const cursedColor = player.options.lightGlyphs ? "white" : "black";
return {
color,
color: this.type === "cursed" ? cursedColor : color,
animation: this.type === "reality" ? "a-reality-glyph-name-cycle 10s infinite" : undefined
};
},

View File

@ -76,9 +76,9 @@ export default {
.replace("\n", "<br>")
.replace("]", "</span>")
.replace(
"[", `<span style="${Theme.current().isDark() || player.options.forceDarkGlyphs
? "text-shadow: white 0 0 0.6rem;"
: ""}
"[", `<span style="${player.options.lightGlyphs
? ""
: "text-shadow: white 0 0 0.6rem;"}
font-weight: bold;">`
);
}

View File

@ -105,7 +105,6 @@ export default {
<div
v-if="show"
:class="classObject"
class="c-modal-away-progress__resources"
@click="hideEntry"
>
<span v-if="isBlackHole">

View File

@ -85,6 +85,14 @@ export default {
? `After importing, will simulate ${formatInt(ticks)} ticks of duration
${TimeSpan.fromMilliseconds((Date.now() - this.player.lastUpdate) / ticks).toStringShort()} each.`
: "This setting will not apply any offline progress after importing.";
},
willLoseCosmetics() {
const currSets = player.reality.glyphs.cosmetics.unlockedFromNG;
const importedSets = this.player.reality?.glyphs.cosmetics.unlockedFromNG ?? [];
return currSets.filter(set => !importedSets.includes(set)).length > 0;
},
willLoseSpeedrun() {
return player.speedrun.isUnlocked && !this.player.speedrun?.isUnlocked;
}
},
mounted() {
@ -159,6 +167,9 @@ export default {
<div v-if="progress.isRealityUnlocked">
Realities: {{ formatPostBreak(player.realities, 2) }}
</div>
<div v-if="progress.hasFullCompletion">
Full game completions: {{ formatInt(player.records.fullGameCompletions) }}
</div>
<div class="c-modal-import__warning">
(Your current save file will be overwritten!)
</div>
@ -179,6 +190,21 @@ export default {
<br>
{{ saveCheckString }}
</div>
<div
v-if="player"
class="c-modal-hard-reset-danger"
>
<div v-if="willLoseCosmetics">
<br>
Glyph cosmetic sets from completing the game are tied to your save.
<br>
Importing this save will cause you to lose some sets.
</div>
<div v-if="willLoseSpeedrun">
<br>
You will lose the ability to do a Speedrun, as this save does not have it unlocked.
</div>
</div>
</div>
<PrimaryButton

View File

@ -5,6 +5,7 @@ import ModalWrapperChoice from "@/components/modals/ModalWrapperChoice";
import PrimaryButton from "@/components/PrimaryButton";
import StudyStringLine from "@/components/modals/StudyStringLine";
import StudyStringPreview from "./time-study-modal-preview/StudyStringPreview";
import StudyTreeInfo from "./StudyTreeInfo";
export default {
@ -13,6 +14,7 @@ export default {
ModalWrapperChoice,
StudyStringLine,
PrimaryButton,
StudyStringPreview,
StudyTreeInfo
},
props: {
@ -232,6 +234,11 @@ export default {
:tree-status="combinedTree"
/>
</template>
<StudyStringPreview
v-if="!deleting"
:show-preview="inputIsValidTree"
:tree-status="combinedTree"
/>
<div v-else-if="hasInput">
Not a valid tree
</div>

View File

@ -30,8 +30,10 @@ export default {
},
setContents() {
const contents = [];
if (this.symbols) contents.push(quantifyInt("symbol", this.symbols.length));
if (this.colors) contents.push(quantifyInt("color scheme", this.colors.length));
// We explicitly pass in x => x as the formatting function in order to override END formatting; if we don't,
// this modal will show END symbols/colors when opened at game completion
if (this.symbols) contents.push(quantify("symbol", this.symbols.length, 0, 0, x => x));
if (this.colors) contents.push(quantify("color scheme", this.colors.length, 0, 0, x => x));
return contents.join(" and ");
},
symbols() {

View File

@ -70,12 +70,16 @@ export default {
EventHub.dispatch(GAME_EVENT.GLYPH_VISUAL_CHANGE);
},
fakeGlyph(type) {
let typeName = "power";
if (type === "reality") typeName = "reality";
if (type === "cursed") typeName = "cursed";
return {
// This are just dummy values to make sure that GlyphComponent doesn't throw errors; only the cosmetic aspects
// will end up being visible in this case anyway (as they override anything type would otherwise show). Type
// looks particularly odd because reality glyphs need that passed in for the color animation, and power is an
// okay placeholder for anything else. We can't pass in type or else it will error out with cosmetic types.
type: type === "reality" ? "reality" : "power",
// looks particularly odd because reality glyphs need that passed in for the color animation, and cursed ones
// are inverted, but power is an okay placeholder for anything else. We can't pass in type or else it will error
// out with cosmetic types.
type: typeName,
strength: 1,
cosmetic: type,
};

View File

@ -68,7 +68,7 @@ export default {
this.isActive = player.reality.glyphs.cosmetics.active;
}
if (this.type === "reality" && !this.isSymbol) this.realityColor = GlyphAppearanceHandler.realityColor;
this.darkKeySwap = player.options.forceDarkGlyphs;
this.darkKeySwap = !player.options.lightGlyphs;
},
select(option) {
if (!this.isSingleGlyph && !this.isActive) return;

View File

@ -19,7 +19,7 @@ export default {
return {
newGlyphs: false,
glyphEffectDots: false,
forceDarkGlyphs: false,
lightGlyphs: false,
glyphInfoType: 0,
showGlyphInfoByDefault: false,
};
@ -38,8 +38,8 @@ export default {
player.options.showHintText.glyphEffectDots = newValue;
EventHub.dispatch(GAME_EVENT.GLYPH_VISUAL_CHANGE);
},
forceDarkGlyphs(newValue) {
player.options.forceDarkGlyphs = newValue;
lightGlyphs(newValue) {
player.options.lightGlyphs = newValue;
EventHub.dispatch(GAME_EVENT.GLYPH_VISUAL_CHANGE);
},
showGlyphInfoByDefault(newValue) {
@ -52,7 +52,7 @@ export default {
const options = player.options;
this.newGlyphs = options.showNewGlyphIcon;
this.glyphEffectDots = options.showHintText.glyphEffectDots;
this.forceDarkGlyphs = options.forceDarkGlyphs;
this.lightGlyphs = options.lightGlyphs;
this.glyphInfoType = options.showHintText.glyphInfoType;
this.showGlyphInfoByDefault = options.showHintText.showGlyphInfoByDefault;
},
@ -81,8 +81,8 @@ export default {
text="Always show Glyph effect dots:"
/>
<ModalOptionsToggleButton
v-model="forceDarkGlyphs"
text="Force dark Glyph background:"
v-model="lightGlyphs"
text="Light Glyph backgrounds:"
/>
<ExpandingControlBox
class="o-primary-btn c-dropdown-btn"

View File

@ -0,0 +1,140 @@
<script>
export default {
name: "PseudoTimeStudyButton",
props: {
setup: {
type: Object,
required: true
},
isNewFromImport: {
type: Boolean,
default: false
}
},
data() {
return {
isUseless: false,
doomedRealityStudy: false,
isBought: false,
};
},
computed: {
study() {
return this.setup.study;
},
styleObject() {
return {
top: `${this.setup.top}rem`,
left: `${this.setup.left}rem`
};
},
classObject() {
return {
"o-pseudo-time-study": true,
"l-time-study": true,
"c-pelle-useless": this.isUseless,
"c-pelle-useless--bought": this.isUseless && this.isBought,
"c-pelle-useless--unavailable": this.isUseless && !this.isBought,
"o-pseudo-time-study--small": this.setup.isSmall,
"o-time-study--unavailable": !this.isBought && !this.isUseless,
"o-time-study--bought": this.isBought && !this.isUseless,
"o-time-study--new-import": this.isNewFromImport && !this.isBought
};
},
pathClass() {
switch (this.study.type) {
case TIME_STUDY_TYPE.NORMAL:
switch (this.setup.path) {
case TIME_STUDY_PATH.ANTIMATTER_DIM: return "o-time-study-antimatter-dim";
case TIME_STUDY_PATH.INFINITY_DIM: return "o-time-study-infinity-dim";
case TIME_STUDY_PATH.TIME_DIM: return "o-time-study-time-dim";
case TIME_STUDY_PATH.ACTIVE: return "o-time-study-active";
case TIME_STUDY_PATH.PASSIVE: return "o-time-study-passive";
case TIME_STUDY_PATH.IDLE: return "o-time-study-idle";
case TIME_STUDY_PATH.LIGHT: return "o-time-study-light";
case TIME_STUDY_PATH.DARK: return "o-time-study-dark";
default: return "o-time-study-normal";
}
case TIME_STUDY_TYPE.ETERNITY_CHALLENGE:
return "o-time-study-eternity-challenge";
case TIME_STUDY_TYPE.DILATION:
if (this.study.id === 6) return "o-time-study-reality";
return "o-time-study-dilation";
case TIME_STUDY_TYPE.TRIAD:
return "o-time-study-triad";
}
return "";
},
studyClass() {
if (this.isUseless) return "";
return `${this.pathClass}--${this.isBought ? "bought" : "unavailable"}`;
},
studyString() {
switch (this.study.type) {
case TIME_STUDY_TYPE.NORMAL: case TIME_STUDY_TYPE.TRIAD: return `${this.study.id}`;
case TIME_STUDY_TYPE.ETERNITY_CHALLENGE: return `EC${this.study.id}`;
}
return "";
}
},
methods: {
update() {
const study = this.study;
this.isUseless = Pelle.uselessTimeStudies.includes(this.study.id) && Pelle.isDoomed;
this.isBought = study.isBought;
this.doomedRealityStudy = study.type === TIME_STUDY_TYPE.DILATION && study.id === 6 && Pelle.isDoomed;
},
}
};
</script>
<template>
<button
:class="[classObject, studyClass]"
:style="styleObject"
>
{{ studyString }}
</button>
</template>
<style scoped>
.o-pseudo-time-study {
width: 2.7rem;
height: 1.5rem;
font-family: Typewriter, serif;
font-size: 0.85rem;
color: black;
border: 0.15rem solid;
border-radius: var(--var-border-radius, 0.2rem);
padding: 0;
transition-duration: 0.2s;
pointer-events: none;
}
.o-pseudo-time-study--small {
width: 1.8rem;
}
.o-time-study-dark--bought {
color: white;
}
.o-time-study--new-import::before {
content: "";
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background: rgba(255, 214, 11, 0.8);
border-radius: var(--var-border-radius, inherit);
animation: a-new-import 3s infinite;
}
@keyframes a-new-import {
0% { opacity: 0; }
50% { opacity: 0.7; }
100% { opacity: 0; }
}
</style>

View File

@ -0,0 +1,83 @@
<script>
export default {
name: "TimeStudyConnection",
props: {
setup: {
type: Object,
required: true
}
},
data() {
return {
isOverridden: false,
isBought: false
};
},
computed: {
classObject() {
const classObject = {
"o-time-study-connection": true,
"o-time-study-connection--bought": this.isBought,
};
let pathClass;
const connection = this.setup.connection;
const from = connection.from;
const to = connection.to;
function pathClassOf(study) {
switch (study.path) {
case TIME_STUDY_PATH.ANTIMATTER_DIM: return "o-time-study-connection--antimatter-dim";
case TIME_STUDY_PATH.INFINITY_DIM: return "o-time-study-connection--infinity-dim";
case TIME_STUDY_PATH.TIME_DIM: return "o-time-study-connection--time-dim";
case TIME_STUDY_PATH.ACTIVE: return "o-time-study-connection--active";
case TIME_STUDY_PATH.PASSIVE: return "o-time-study-connection--passive";
case TIME_STUDY_PATH.IDLE: return "o-time-study-connection--idle";
default: return undefined;
}
}
switch (to.type) {
case TIME_STUDY_TYPE.NORMAL:
pathClass = pathClassOf(to) || pathClassOf(from);
break;
case TIME_STUDY_TYPE.ETERNITY_CHALLENGE:
pathClass = "o-time-study-connection--eternity-challenge";
break;
case TIME_STUDY_TYPE.DILATION:
pathClass = "o-time-study-connection--dilation";
break;
}
if (pathClass !== undefined) {
classObject[pathClass] = true;
}
return classObject;
}
},
methods: {
update() {
this.isOverridden = this.setup.connection.isOverridden;
this.isBought = this.setup.isBought;
},
percents(value) {
return `${value * 100}%`;
}
}
};
</script>
<template>
<line
v-if="!isOverridden"
:x1="percents(setup.x1)"
:y1="percents(setup.y1)"
:x2="percents(setup.x2)"
:y2="percents(setup.y2)"
:class="classObject"
/>
</template>
<style scoped>
.o-time-study-connection {
/* This one should be px, because it rem svg behaves weirdly under scale */
stroke-width: 2px;
}
</style>

View File

@ -0,0 +1,140 @@
<script>
import PseudoTimeStudyButton from "./PseudoTimeStudyButton";
import PseudoTimeStudyConnection from "./PseudoTimeStudyConnection";
import { STUDY_TREE_LAYOUT_TYPE, TimeStudyTreeLayout } from "@/components/tabs/time-studies/time-study-tree-layout";
export default {
name: "TimeStudiesTab",
components: {
PseudoTimeStudyButton,
PseudoTimeStudyConnection,
},
props: {
treeStatus: {
type: [Object, Boolean],
required: true
},
showPreview: {
type: Boolean,
default: true
}
},
data() {
return {
layoutType: STUDY_TREE_LAYOUT_TYPE.NORMAL,
vLevel: 0,
renderedStudyCount: 0,
isEnslaved: false,
delayTimer: 0,
};
},
computed: {
layout() {
return TimeStudyTreeLayout.create(this.layoutType, 0.15);
},
studies() {
return this.layout.studies;
},
newStudies() {
// NewStudies is formatted using makeEnumeration so we need to convert it back to an array
return this.treeStatus.newStudies.replace(", and ", ", ").replace(" and ", ", ").split(", ");
},
connections() {
return this.layout.connections;
},
treeStyleObject() {
return {
width: `${this.layout.width}rem`,
height: `${this.layout.height}rem`
};
},
respecClassObject() {
return {
"o-primary-btn--subtab-option": true,
"o-primary-btn--respec-active": this.respec
};
}
},
watch: {
vLevel() {
// When vLevel changes, we recompute the study tree because of triad studies
this.$recompute("layout");
}
},
methods: {
update() {
this.layoutType = STUDY_TREE_LAYOUT_TYPE.current;
this.vLevel = Ra.pets.v.level;
this.isEnslaved = Enslaved.isRunning || Date.now() - this.delayTimer < 1000;
},
studyComponent(study) {
switch (study.type) {
case TIME_STUDY_TYPE.NORMAL: return NormalTimeStudy;
case TIME_STUDY_TYPE.ETERNITY_CHALLENGE: return ECTimeStudy;
case TIME_STUDY_TYPE.DILATION: return DilationTimeStudy;
case TIME_STUDY_TYPE.TRIAD: return TriadTimeStudy;
}
throw "Unknown Time Study type";
},
studyString(study) {
switch (study.type) {
case TIME_STUDY_TYPE.NORMAL: case TIME_STUDY_TYPE.TRIAD: return `${study.id}`;
case TIME_STUDY_TYPE.ETERNITY_CHALLENGE: return `EC${study.id}`;
}
return "Dilation Study";
}
}
};
</script>
<template>
<div class="l-study-string-preview__tree--wrapper">
<div
v-if="showPreview"
class="l-time-study-tree l-study-string-preview__tree"
:style="treeStyleObject"
>
<PseudoTimeStudyButton
v-for="setup in studies"
:key="setup.study.type.toString() + setup.study.id.toString()"
:setup="setup"
:is-new-from-import="newStudies.includes(studyString(setup.study))"
/>
<svg
:style="treeStyleObject"
class="l-time-study-connection"
>
<PseudoTimeStudyConnection
v-for="(setup, index) in connections"
:key="'connection' + index"
:setup="setup"
/>
</svg>
</div>
<span
v-else
class="c-unavailable-warning"
>
Preview Unavailable
</span>
</div>
</template>
<style scoped>
.l-study-string-preview__tree--wrapper {
display: flex;
overflow-y: auto;
width: 30rem;
height: 15rem;
position: relative;
justify-content: center;
border: var(--color-text) solid var(--var-border-width, 0.3rem);
border-radius: var(--var-border-radius, 0.3rem);
margin: auto;
}
.c-unavailable-warning {
align-self: center;
}
</style>

View File

@ -33,6 +33,10 @@ export default {
},
created() {
this.currentScriptID = player.reality.automator.state.editorScript;
// Deleted script names potentially persist within the vue component unless we clear them
this.on$(GAME_EVENT.AUTOMATOR_SAVE_CHANGED, () => {
this.$recompute("scripts");
});
},
methods: {
update() {

View File

@ -66,6 +66,12 @@ export default {
},
isEmpty() {
return !this.isRecent(this.lastNotEmptyAt);
},
disabledText() {
if (!this.resource.isBase) return `Total effect inactive, disabled, or reduced to ${formatX(1)}`;
return Decimal.eq(this.resource.mult, 0)
? `You cannot gain this resource (prestige requirement not reached)`
: `You have no multipliers for this resource (will gain ${format(1)} on prestige)`;
}
},
methods: {
@ -324,9 +330,10 @@ export default {
v-if="isEmpty"
class="c-no-effect"
>
No Active Multipliers
No Active Effects
<br>
Total effect disabled or reduced to {{ formatX(1) }}.
<br>
{{ disabledText }}
</div>
<div
v-for="(entry, index) in entries"
@ -359,6 +366,14 @@ export default {
</div>
</div>
</div>
<div v-if="resource === 'AD_total'">
<br>
"Base AD Production" is the amount of Antimatter that you would be producing with your current AD upgrades
as if you had waited a fixed amount of time ({{ formatInt(10) }}-{{ formatInt(40) }} seconds depending on
your AD count) after a Sacrifice. This may misrepresent your actual production if your ADs have been
producing for a while, but the relative mismatch will become smaller as you progress further in the game
and numbers become larger.
</div>
</div>
</div>
</template>

View File

@ -31,19 +31,19 @@ class TimeStudyRowLayout {
}
export class TimeStudyTreeLayout {
constructor(type) {
this.spacing = 4;
constructor(type, scaling = 1) {
this.spacing = 4 * scaling;
const normalRowLayout = new TimeStudyRowLayout({
itemWidth: 18,
itemHeight: 10,
spacing: 3
itemWidth: 18 * scaling,
itemHeight: 10 * scaling,
spacing: 3 * scaling
});
const wideRowLayout = new TimeStudyRowLayout({
itemWidth: 12,
itemHeight: 10,
spacing: 0.6
itemWidth: 12 * scaling,
itemHeight: 10 * scaling,
spacing: 0.6 * scaling
});
const normalRow = (...items) => new TimeStudyRow(normalRowLayout, items);
const wideRow = (...items) => new TimeStudyRow(wideRowLayout, items, true);
@ -195,12 +195,12 @@ export class TimeStudyTreeLayout {
return heightNoSpacing + rows.length * this.spacing;
}
static create(type) {
static create(type, scaling = 1) {
if (this._instances === undefined) {
this._instances = [];
}
const layout = new TimeStudyTreeLayout(type);
this._instances[type] = layout;
const layout = new TimeStudyTreeLayout(type, scaling);
this._instances[`${type}__${scaling}`] = layout;
return layout;
}
}