Merge branch 'master' into amoled-theme
|
@ -142,12 +142,6 @@ export const Achievements = {
|
|||
return;
|
||||
}
|
||||
if (Achievements.preReality.every(a => a.isUnlocked)) return;
|
||||
if (Perk.achievementGroup5.isBought) {
|
||||
for (const achievement of Achievements.preReality) {
|
||||
achievement.unlock(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
player.reality.achTimer += diff;
|
||||
if (player.reality.achTimer < this.period) return;
|
||||
|
|
|
@ -72,6 +72,8 @@ import SwitchAutomatorEditorModal from "@/components/modals/SwitchAutomatorEdito
|
|||
import UiChoiceModal from "@/components/modals/UiChoiceModal";
|
||||
import UndoGlyphModal from "@/components/modals/UndoGlyphModal";
|
||||
|
||||
import S12GamesModal from "@/components/modals/secret-themes/S12GamesModal";
|
||||
|
||||
let nextModalID = 0;
|
||||
export class Modal {
|
||||
constructor(component, priority = 0, closeEvent) {
|
||||
|
@ -255,6 +257,8 @@ Modal.sacrifice = new Modal(SacrificeModal, 1, GAME_EVENT.DIMBOOST_AFTER);
|
|||
Modal.breakInfinity = new Modal(BreakInfinityModal, 1, GAME_EVENT.ETERNITY_RESET_AFTER);
|
||||
Modal.respecIAP = new Modal(RespecIAPModal);
|
||||
|
||||
Modal.s12Games = new Modal(S12GamesModal);
|
||||
|
||||
function getSaveInfo(save) {
|
||||
const resources = {
|
||||
realTimePlayed: 0,
|
||||
|
@ -287,13 +291,15 @@ function getSaveInfo(save) {
|
|||
resources.eternityPoints.copyFrom(new Decimal(save.eternityPoints));
|
||||
resources.realityMachines.copyFrom(new Decimal(save.reality?.realityMachines));
|
||||
resources.imaginaryMachines = save.reality?.iMCap ?? 0;
|
||||
resources.dilatedTime.copyFrom(new Decimal(save.dilation.dilatedTime));
|
||||
// Use max DT instead of current DT because spending it can cause it to drop and trigger the conflict modal
|
||||
// unnecessarily. We only use current DT as a fallback (eg. loading a save from pre-reality versions)
|
||||
resources.dilatedTime.copyFrom(new Decimal(save.records?.thisReality.maxDT ?? (save.dilation?.dilatedTime ?? 0)));
|
||||
resources.bestLevel = save.records?.bestReality.glyphLevel ?? 0;
|
||||
resources.pelleAM.copyFrom(new Decimal(save.celestials?.pelle.records.totalAntimatter));
|
||||
resources.remnants = save.celestials?.pelle.remnants ?? 0;
|
||||
resources.realityShards.copyFrom(new Decimal(save.celestials?.pelle.realityShards));
|
||||
resources.pelleLore = save.celestials?.pelle.quoteBits ?? 0;
|
||||
resources.saveName = save.options.saveFileName ?? "";
|
||||
resources.saveName = save.options?.saveFileName ?? "";
|
||||
resources.compositeProgress = ProgressChecker.getCompositeProgress(save);
|
||||
|
||||
return resources;
|
||||
|
|
|
@ -4,7 +4,7 @@ export const Theme = function Theme(name, config) {
|
|||
this.name = name;
|
||||
|
||||
this.isDark = function() {
|
||||
return this.isDefault()
|
||||
return (this.isDefault() || name === "S12")
|
||||
? player.options.newUI
|
||||
: config.isDark;
|
||||
};
|
||||
|
@ -88,6 +88,7 @@ Theme.secretThemeIndex = function(name) {
|
|||
"dba8336cd3224649d07952b00045a6ec3c8df277aa8a0a0e3e7c2aaa77f1fbb9",
|
||||
"73de8a7f9efa1cbffc80a8effc9891a799127cd204b3a8b023bea8f513ed4753",
|
||||
"f3a71114261b4af6517a53f89bf0c6b56bb81b6f0e931d0e0d71249eb196628c",
|
||||
"1248689171faaa0abb68279199a8d2eb232dba10d2dacb79a705f680b6862c0e",
|
||||
];
|
||||
const sha = sha512_256(name.toUpperCase());
|
||||
return secretThemes.indexOf(sha);
|
||||
|
@ -114,6 +115,10 @@ Theme.tryUnlock = function(name) {
|
|||
SecretAchievement(25).unlock();
|
||||
if (!isAlreadyUnlocked) {
|
||||
GameUI.notify.success(`You have unlocked the ${name.capitalize()} theme!`, 5000);
|
||||
if (Theme.current().isAnimated) {
|
||||
setTimeout(Modal.message.show(`This secret theme has animations. If they are giving you performance issues,
|
||||
you can turn them off in the Options/Visual tab to reduce lag.`), 100);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
@ -150,7 +155,8 @@ export const Themes = {
|
|||
Theme.create("S8", { metro: true, secret: true, }),
|
||||
Theme.create("S9", { secret: true, }),
|
||||
Theme.create("S10", { dark: true, metro: true, animated: true, secret: true, }),
|
||||
Theme.create("S11", { dark: true, secret: true, }),
|
||||
Theme.create("S11", { dark: true, animated: true, secret: true, }),
|
||||
Theme.create("S12", { secret: true, }),
|
||||
/* eslint-enable no-multi-spaces */
|
||||
],
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ Autobuyer.eternity = new class EternityAutobuyerState extends AutobuyerState {
|
|||
return (considerMilestoneReached || EternityMilestone.autoEternities.isReached) &&
|
||||
!Player.isInAnyChallenge && !player.dilation.active &&
|
||||
player.auto.autobuyersOn && this.data.isActive &&
|
||||
this.mode === AUTO_ETERNITY_MODE.AMOUNT &&
|
||||
this.amount.equals(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,12 @@ Autobuyer.epMult = new class EPMultAutobuyerState extends AutobuyerState {
|
|||
}
|
||||
|
||||
tick() {
|
||||
// While the active check is normally automatically handled with the global autobuyer ticking method, we also
|
||||
// call this from the TD autobuyers in order to make sure this is executed before TDs are purchased. Simply
|
||||
// reordering the autobuyer call order is undesirable because much of the codebase relies on autobuyers being
|
||||
// grouped as they are, and many other autobuyers in the 5xEP group must execute *after* dimensions
|
||||
if (!this.isActive) return;
|
||||
applyEU2();
|
||||
EternityUpgrade.epMult.buyMax();
|
||||
}
|
||||
}();
|
||||
|
|
|
@ -41,12 +41,30 @@ Autobuyer.reality = new class RealityAutobuyerState extends AutobuyerState {
|
|||
this.data.glyph = value;
|
||||
}
|
||||
|
||||
get time() {
|
||||
return this.data.time;
|
||||
}
|
||||
|
||||
set time(value) {
|
||||
this.data.time = value;
|
||||
}
|
||||
|
||||
get shard() {
|
||||
return this.data.shard;
|
||||
}
|
||||
|
||||
set shard(value) {
|
||||
this.data.shard = value;
|
||||
}
|
||||
|
||||
toggleMode() {
|
||||
this.mode = [
|
||||
AUTO_REALITY_MODE.RM,
|
||||
AUTO_REALITY_MODE.GLYPH,
|
||||
AUTO_REALITY_MODE.EITHER,
|
||||
AUTO_REALITY_MODE.BOTH
|
||||
AUTO_REALITY_MODE.BOTH,
|
||||
AUTO_REALITY_MODE.TIME,
|
||||
AUTO_REALITY_MODE.RELIC_SHARD
|
||||
]
|
||||
.nextSibling(this.mode);
|
||||
}
|
||||
|
@ -74,6 +92,12 @@ Autobuyer.reality = new class RealityAutobuyerState extends AutobuyerState {
|
|||
case AUTO_REALITY_MODE.BOTH:
|
||||
proc = rmProc && glyphProc;
|
||||
break;
|
||||
case AUTO_REALITY_MODE.TIME:
|
||||
proc = player.records.thisReality.realTime / 1000 > this.time;
|
||||
break;
|
||||
case AUTO_REALITY_MODE.RELIC_SHARD:
|
||||
proc = Effarig.shardsGained > this.shard;
|
||||
break;
|
||||
}
|
||||
if (proc) autoReality();
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ class TimeDimensionAutobuyerState extends IntervaledAutobuyerState {
|
|||
}
|
||||
|
||||
tick() {
|
||||
applyEU2();
|
||||
Autobuyer.epMult.tick();
|
||||
const tier = this.tier;
|
||||
if (!TimeDimension(tier).isAvailableForPurchase) return;
|
||||
super.tick();
|
||||
|
|
|
@ -856,9 +856,12 @@ export const AutomatorBackend = {
|
|||
},
|
||||
|
||||
start(scriptID = this.state.topLevelScript, initialMode = AUTOMATOR_MODE.RUN, compile = true) {
|
||||
// Automator execution behaves oddly across new games, so we explicitly stop it from running if not unlocked
|
||||
if (!Player.automatorUnlocked) return;
|
||||
this.hasJustCompleted = false;
|
||||
this.state.topLevelScript = scriptID;
|
||||
const scriptObject = this.findScript(scriptID);
|
||||
if (!scriptObject) return;
|
||||
if (compile) scriptObject.compile();
|
||||
if (scriptObject.commands) {
|
||||
this.reset(scriptObject.commands);
|
||||
|
|
|
@ -57,15 +57,15 @@ export const AutomatorCommands = ((() => {
|
|||
let addedECs, gainedEP;
|
||||
switch (layer) {
|
||||
case "INFINITY":
|
||||
return `${format(player.records.lastTenInfinities[0][1], 2)} IP`;
|
||||
return `${format(player.records.recentInfinities[0][1], 2)} IP`;
|
||||
case "ETERNITY":
|
||||
addedECs = AutomatorData.lastECCompletionCount;
|
||||
gainedEP = `${format(player.records.lastTenEternities[0][1], 2)} EP`;
|
||||
gainedEP = `${format(player.records.recentEternities[0][1], 2)} EP`;
|
||||
return addedECs === 0
|
||||
? `${gainedEP}`
|
||||
: `${gainedEP}, ${addedECs} completions`;
|
||||
case "REALITY":
|
||||
return `${format(player.records.lastTenRealities[0][1], 2)} RM`;
|
||||
return `${format(player.records.recentRealities[0][1], 2)} RM`;
|
||||
default:
|
||||
throw Error(`Unrecognized prestige ${layer} in Automator event log`);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { GameDatabase } from "./secret-formula/game-database";
|
|||
class AwayProgress {
|
||||
constructor(config) {
|
||||
this.name = config.name;
|
||||
this.forcedName = config.forcedName;
|
||||
this.isUnlocked = config.isUnlocked;
|
||||
this.awayOption = config.awayOption ?? this.name;
|
||||
this.showOption = config.showOption ?? true;
|
||||
|
@ -32,6 +33,7 @@ class AwayProgress {
|
|||
}
|
||||
|
||||
get formatName() {
|
||||
if (this.forcedName) return this.forcedName;
|
||||
// Format the camelCase name to Title Case, with spaces added before the capital letters
|
||||
return this.name
|
||||
.replace(/[A-Z]/gu, match => ` ${match}`)
|
||||
|
|
|
@ -27,7 +27,9 @@ function handleChallengeCompletion() {
|
|||
export function manualBigCrunchResetRequest() {
|
||||
if (!Player.canCrunch) return;
|
||||
if (GameEnd.creditsEverClosed) return;
|
||||
if (player.options.confirmations.bigCrunch) {
|
||||
// We show the modal under two conditions - on the first ever infinity (to explain the mechanic) and
|
||||
// post-break (to show total IP and infinities gained)
|
||||
if (player.options.confirmations.bigCrunch && (!PlayerProgress.infinityUnlocked() || player.break)) {
|
||||
Modal.bigCrunch.show();
|
||||
} else {
|
||||
bigCrunchResetRequest();
|
||||
|
@ -177,7 +179,7 @@ export function preProductionGenerateIP(diff) {
|
|||
genCount = Math.floor(player.partInfinityPoint);
|
||||
player.partInfinityPoint -= genCount;
|
||||
}
|
||||
let gainedPerGen = InfinityUpgrade.ipGen.effectValue;
|
||||
let gainedPerGen = player.records.bestInfinity.time >= 999999999999 ? DC.D0 : InfinityUpgrade.ipGen.effectValue;
|
||||
if (Laitela.isRunning) gainedPerGen = dilatedValueOf(gainedPerGen);
|
||||
const gainedThisTick = new Decimal(genCount).times(gainedPerGen);
|
||||
Currency.infinityPoints.add(gainedThisTick);
|
||||
|
|
|
@ -197,16 +197,7 @@ class BlackHoleState {
|
|||
if (Enslaved.autoReleaseTick < 3) return `<i class="fas fa-compress-arrows-alt u-fa-padding"></i> Pulsing`;
|
||||
return `<i class="fas fa-expand-arrows-alt u-fa-padding"></i> Pulsing`;
|
||||
}
|
||||
if (Enslaved.isStoringGameTime) {
|
||||
if (Ra.unlocks.adjustableStoredTime.canBeApplied) {
|
||||
const storedTimeWeight = player.celestials.enslaved.storedFraction;
|
||||
if (storedTimeWeight !== 0) {
|
||||
return `<i class="fas fa-compress-arrows-alt"></i> Charging (${formatPercents(storedTimeWeight, 1)})`;
|
||||
}
|
||||
} else {
|
||||
return `<i class="fas fa-compress-arrows-alt"></i> Charging`;
|
||||
}
|
||||
}
|
||||
if (Enslaved.isStoringGameTime) return `<i class="fas fa-compress-arrows-alt"></i> Charging`;
|
||||
if (BlackHoles.areNegative) return `<i class="fas fa-caret-left"></i> Inverted`;
|
||||
if (BlackHoles.arePaused) return `<i class="fas fa-pause"></i> Paused`;
|
||||
if (this.isPermanent) return `<i class="fas fa-infinity"></i> Permanent`;
|
||||
|
|
|
@ -48,14 +48,14 @@ export const GameCache = {
|
|||
worstChallengeTime: new Lazy(() => player.challenge.normal.bestTimes.max()),
|
||||
|
||||
bestRunIPPM: new Lazy(() =>
|
||||
player.records.lastTenInfinities
|
||||
.map(run => ratePerMinute(run[1], run[0]))
|
||||
player.records.recentInfinities
|
||||
.map(run => ratePerMinute(run[2], run[0]))
|
||||
.reduce(Decimal.maxReducer)
|
||||
),
|
||||
|
||||
averageRealTimePerEternity: new Lazy(() => player.records.lastTenEternities
|
||||
.map(run => run[3])
|
||||
.reduce(Number.sumReducer) / (1000 * player.records.lastTenEternities.length)),
|
||||
averageRealTimePerEternity: new Lazy(() => player.records.recentEternities
|
||||
.map(run => run[1])
|
||||
.reduce(Number.sumReducer) / (1000 * player.records.recentEternities.length)),
|
||||
|
||||
tickSpeedMultDecrease: new Lazy(() => 10 - Effects.sum(
|
||||
BreakInfinityUpgrade.tickspeedCostMult,
|
||||
|
@ -76,8 +76,7 @@ export const GameCache = {
|
|||
Perk.achievementGroup1,
|
||||
Perk.achievementGroup2,
|
||||
Perk.achievementGroup3,
|
||||
Perk.achievementGroup4,
|
||||
Perk.achievementGroup5
|
||||
Perk.achievementGroup4
|
||||
)).totalMilliseconds),
|
||||
|
||||
buyablePerks: new Lazy(() => Perks.all.filter(p => p.canBeBought)),
|
||||
|
|
|
@ -82,7 +82,8 @@ class VRunUnlockState extends GameMechanicState {
|
|||
Decimal.gte(playerData.runRecords[this.id], this.conditionValue)) {
|
||||
if (!V.isFlipped && this.config.isHard) continue;
|
||||
this.completions++;
|
||||
GameUI.notify.success(`You have unlocked V-Achievement '${this.config.name}' tier ${this.completions}`);
|
||||
GameUI.notify.success(`You have unlocked V-Achievement
|
||||
'${this.config.name}' tier ${formatInt(this.completions)}`);
|
||||
|
||||
V.updateTotalRunUnlocks();
|
||||
|
||||
|
|
|
@ -51,10 +51,10 @@ export const Effarig = {
|
|||
}
|
||||
},
|
||||
get glyphEffectAmount() {
|
||||
const genEffectBitmask = Glyphs.activeList
|
||||
const genEffectBitmask = Glyphs.activeWithoutCompanion
|
||||
.filter(g => generatedTypes.includes(g.type))
|
||||
.reduce((prev, curr) => prev | curr.effects, 0);
|
||||
const nongenEffectBitmask = Glyphs.activeList
|
||||
const nongenEffectBitmask = Glyphs.activeWithoutCompanion
|
||||
.filter(g => !generatedTypes.includes(g.type))
|
||||
.reduce((prev, curr) => prev | curr.effects, 0);
|
||||
return countValuesFromBitmask(genEffectBitmask) + countValuesFromBitmask(nongenEffectBitmask);
|
||||
|
|
|
@ -44,9 +44,6 @@ export const Enslaved = {
|
|||
if (!this.canModifyGameTimeStorage) return;
|
||||
player.celestials.enslaved.isStoring = !player.celestials.enslaved.isStoring;
|
||||
player.celestials.enslaved.isStoringReal = false;
|
||||
if (!Ra.unlocks.adjustableStoredTime.canBeApplied) {
|
||||
player.celestials.enslaved.storedFraction = 1;
|
||||
}
|
||||
},
|
||||
toggleStoreReal() {
|
||||
if (!this.canModifyRealTimeStorage && !this.isStoredRealTimeCapped) return;
|
||||
|
@ -69,8 +66,9 @@ export const Enslaved = {
|
|||
},
|
||||
// We assume that the situations where you can't modify time storage settings (of either type) are exactly the cases
|
||||
// where they have also been explicitly disabled via other game mechanics. This also reduces UI boilerplate code.
|
||||
// Note that we force time storage when auto-releasing, as not doing so caused a lot of poor usability issues
|
||||
get isStoringGameTime() {
|
||||
return this.canModifyGameTimeStorage && player.celestials.enslaved.isStoring;
|
||||
return this.canModifyGameTimeStorage && (this.isAutoReleasing || player.celestials.enslaved.isStoring);
|
||||
},
|
||||
get isStoringRealTime() {
|
||||
return this.canModifyRealTimeStorage && player.celestials.enslaved.isStoringReal;
|
||||
|
@ -171,6 +169,9 @@ export const Enslaved = {
|
|||
get isCompleted() {
|
||||
return player.celestials.enslaved.completed;
|
||||
},
|
||||
get canTickHintTimer() {
|
||||
return !EnslavedProgress.hintsUnlocked.hasProgress && Enslaved.has(ENSLAVED_UNLOCKS.RUN) && !Enslaved.isCompleted;
|
||||
},
|
||||
get isUnlocked() {
|
||||
return EffarigUnlock.eternity.isUnlocked;
|
||||
},
|
||||
|
|
|
@ -58,6 +58,61 @@ export const Pelle = {
|
|||
// Suppress the randomness for this form
|
||||
possessiveName: "Pelle's",
|
||||
|
||||
// This is called upon initial Dooming and after every Armageddon when using the modal
|
||||
initializeRun() {
|
||||
if (this.isDoomed) {
|
||||
Pelle.armageddon(true);
|
||||
return;
|
||||
}
|
||||
|
||||
Glyphs.harshAutoClean();
|
||||
if (!Glyphs.unequipAll()) {
|
||||
Modal.message.show(`Dooming your Reality will unequip your Glyphs. Some of your
|
||||
Glyphs could not be unequipped due to lack of inventory space.`, 1);
|
||||
return;
|
||||
}
|
||||
Glyphs.harshAutoClean();
|
||||
for (const type of BASIC_GLYPH_TYPES) Glyphs.addToInventory(GlyphGenerator.doomedGlyph(type));
|
||||
Glyphs.refreshActive();
|
||||
player.options.confirmations.glyphReplace = true;
|
||||
player.reality.automator.state.repeat = false;
|
||||
player.reality.automator.state.forceRestart = false;
|
||||
if (BlackHoles.arePaused) BlackHoles.togglePause();
|
||||
player.celestials.pelle.doomed = true;
|
||||
Pelle.armageddon(false);
|
||||
respecTimeStudies(true);
|
||||
Currency.infinityPoints.reset();
|
||||
player.IPMultPurchases = 0;
|
||||
Autobuyer.bigCrunch.mode = AUTO_CRUNCH_MODE.AMOUNT;
|
||||
disChargeAll();
|
||||
clearCelestialRuns();
|
||||
|
||||
// Force-enable the group toggle for AD autobuyers to be active; whether or not they can actually tick
|
||||
// is still handled through if the autobuyers are unlocked at all. This fixes an odd edge case where the player
|
||||
// enters cel7 with AD autobuyers disabled - AD autobuyers need to be reupgraded, but the UI component
|
||||
// for the group toggle is hidden until they're all re-upgraded to the max again.
|
||||
player.auto.antimatterDims.isActive = true;
|
||||
|
||||
player.buyUntil10 = true;
|
||||
player.records.realTimeDoomed = 0;
|
||||
for (const res of AlchemyResources.all) res.amount = 0;
|
||||
AutomatorBackend.stop();
|
||||
|
||||
// Force-unhide all tabs except for the shop tab, for which we retain the hide state instead
|
||||
const shopTab = ~1 & (1 << GameDatabase.tabs.find(t => t.key === "shop").id);
|
||||
player.options.hiddenTabBits &= shopTab;
|
||||
|
||||
// Force unhide MOST subtabs, although some of the tabs get ignored since they don't contain any
|
||||
// meaningful interactable gameplay elements in Doomed
|
||||
const tabsToIgnore = ["statistics", "achievements", "reality", "celestials"];
|
||||
const ignoredIDs = GameDatabase.tabs.filter(t => tabsToIgnore.includes(t.key)).map(t => t.id);
|
||||
for (let tabIndex = 0; tabIndex < GameDatabase.tabs.length; tabIndex++) {
|
||||
player.options.hiddenSubtabBits[tabIndex] &= ignoredIDs.includes(tabIndex) ? -1 : 0;
|
||||
}
|
||||
Pelle.quotes.initial.show();
|
||||
GameStorage.save(true);
|
||||
},
|
||||
|
||||
get displayName() {
|
||||
return Date.now() % 4000 > 500 ? "Pelle" : wordShift.randomCrossWords("Pelle");
|
||||
},
|
||||
|
@ -120,8 +175,8 @@ export const Pelle = {
|
|||
},
|
||||
|
||||
get disabledAchievements() {
|
||||
return [164, 156, 143, 142, 141, 137, 134, 133, 132, 125, 118, 117, 111, 104, 103, 93, 92, 91, 87, 85, 78, 76,
|
||||
74, 65, 55, 54, 37];
|
||||
return [164, 156, 143, 142, 141, 137, 134, 133, 132, 125, 118, 117, 113, 111, 104, 103, 93, 92, 91, 87, 85, 78,
|
||||
76, 74, 65, 55, 54, 37];
|
||||
},
|
||||
|
||||
get uselessInfinityUpgrades() {
|
||||
|
@ -193,7 +248,7 @@ export const Pelle = {
|
|||
case undefined:
|
||||
return "No Glyph equipped!";
|
||||
default:
|
||||
return "";
|
||||
return "You cannot equip this Glyph while Doomed!";
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ class AlchemyResourceState extends GameMechanicState {
|
|||
}
|
||||
|
||||
get lockText() {
|
||||
return `${this.unlockedWith.name} Level ${this.unlockedAt}`;
|
||||
return `${this.unlockedWith.name} Level ${formatInt(this.unlockedAt)}`;
|
||||
}
|
||||
|
||||
get isCustomEffect() {
|
||||
|
|
|
@ -280,7 +280,9 @@ export const Ra = {
|
|||
// Returns a string containing a time estimate for gaining a specific amount of exp (UI only)
|
||||
timeToGoalString(pet, expToGain) {
|
||||
// Quadratic formula for growth (uses constant growth for a = 0)
|
||||
const a = Ra.productionPerMemoryChunk * pet.memoryUpgradeCurrentMult * pet.memoryChunksPerSecond / 2;
|
||||
const a = Enslaved.isStoringRealTime
|
||||
? 0
|
||||
: Ra.productionPerMemoryChunk * pet.memoryUpgradeCurrentMult * pet.memoryChunksPerSecond / 2;
|
||||
const b = Ra.productionPerMemoryChunk * pet.memoryUpgradeCurrentMult * pet.memoryChunks;
|
||||
const c = -expToGain;
|
||||
const estimate = a === 0
|
||||
|
@ -374,7 +376,7 @@ export const Ra = {
|
|||
},
|
||||
get momentumValue() {
|
||||
const hoursFromUnlock = TimeSpan.fromMilliseconds(player.celestials.ra.momentumTime).totalHours;
|
||||
return Math.clampMax(1 + 0.002 * hoursFromUnlock, AlchemyResource.momentum.effectValue);
|
||||
return Math.clampMax(1 + 0.005 * hoursFromUnlock, AlchemyResource.momentum.effectValue);
|
||||
},
|
||||
quotes: Quotes.ra,
|
||||
symbol: "<i class='fas fa-sun'></i>"
|
||||
|
|
|
@ -215,7 +215,9 @@ window.AUTO_REALITY_MODE = {
|
|||
RM: 0,
|
||||
GLYPH: 1,
|
||||
EITHER: 2,
|
||||
BOTH: 3
|
||||
BOTH: 3,
|
||||
TIME: 4,
|
||||
RELIC_SHARD: 5,
|
||||
};
|
||||
|
||||
// Free tickspeed multiplier with TS171. Shared here because formatting glyph effects depends on it
|
||||
|
@ -343,9 +345,10 @@ window.GLYPH_SIDEBAR_MODE = {
|
|||
|
||||
window.AUTO_SORT_MODE = {
|
||||
NONE: 0,
|
||||
POWER: 1,
|
||||
EFFECT: 2,
|
||||
SCORE: 3
|
||||
LEVEL: 1,
|
||||
POWER: 2,
|
||||
EFFECT: 3,
|
||||
SCORE: 4
|
||||
};
|
||||
|
||||
window.AUTO_GLYPH_SCORE = {
|
||||
|
|
|
@ -8,10 +8,12 @@ export function animateAndDilate() {
|
|||
setTimeout(startDilatedEternity, 1000);
|
||||
}
|
||||
|
||||
export function animateAndUndilate() {
|
||||
// eslint-disable-next-line no-empty-function
|
||||
export function animateAndUndilate(callback) {
|
||||
FullScreenAnimationHandler.display("a-undilate", 2);
|
||||
setTimeout(() => {
|
||||
eternity(false, false, { switchingDilation: true });
|
||||
if (callback) callback();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
|
@ -174,8 +176,8 @@ export function getTP(antimatter, requireEternity) {
|
|||
return getBaseTP(antimatter, requireEternity).times(tachyonGainMultiplier());
|
||||
}
|
||||
|
||||
// Returns the amount of TP gained, subtracting out current TP; used only for displaying gained TP
|
||||
// and for "exit dilation" button (saying whether you need more antimatter)
|
||||
// Returns the amount of TP gained, subtracting out current TP; used for displaying gained TP, text on the
|
||||
// "exit dilation" button (saying whether you need more antimatter), and in last 10 eternities
|
||||
export function getTachyonGain(requireEternity) {
|
||||
return getTP(Currency.antimatter.value, requireEternity).minus(Currency.tachyonParticles.value).clampMin(0);
|
||||
}
|
||||
|
|
|
@ -270,7 +270,7 @@ function buyUntilTen(tier) {
|
|||
}
|
||||
|
||||
export function maxAll() {
|
||||
if (Laitela.continuumActive || Currency.antimatter.gt(Player.infinityLimit)) return;
|
||||
if (Laitela.continuumActive) return;
|
||||
|
||||
player.requirementChecks.infinity.maxAll = true;
|
||||
|
||||
|
@ -297,7 +297,8 @@ export function buyMaxDimension(tier, bulk = Infinity) {
|
|||
}
|
||||
|
||||
// Buy any remaining until 10 before attempting to bulk-buy
|
||||
if (Currency.antimatter.purchase(cost)) {
|
||||
if (dimension.currencyAmount.gte(cost)) {
|
||||
dimension.currencyAmount = dimension.currencyAmount.minus(cost);
|
||||
buyUntilTen(tier);
|
||||
bulkLeft--;
|
||||
}
|
||||
|
@ -307,7 +308,9 @@ export function buyMaxDimension(tier, bulk = Infinity) {
|
|||
// Buy in a while loop in order to properly trigger abnormal price increases
|
||||
if (NormalChallenge(9).isRunning || InfinityChallenge(5).isRunning) {
|
||||
while (dimension.isAffordableUntil10 && dimension.cost.lt(goal) && bulkLeft > 0) {
|
||||
Currency.antimatter.subtract(dimension.costUntil10);
|
||||
// We can use dimension.currencyAmount or Currency.antimatter here, they're the same,
|
||||
// but it seems safest to use dimension.currencyAmount for consistency.
|
||||
dimension.currencyAmount = dimension.currencyAmount.minus(dimension.costUntil10);
|
||||
buyUntilTen(tier);
|
||||
bulkLeft--;
|
||||
}
|
||||
|
@ -328,11 +331,6 @@ export function buyMaxDimension(tier, bulk = Infinity) {
|
|||
dimension.currencyAmount = dimension.currencyAmount.minus(Decimal.pow10(maxBought.logPrice));
|
||||
}
|
||||
|
||||
|
||||
export function canAfford(cost) {
|
||||
return (player.break || cost.lt(Decimal.NUMBER_MAX_VALUE)) && Currency.antimatter.gte(cost);
|
||||
}
|
||||
|
||||
class AntimatterDimensionState extends DimensionState {
|
||||
constructor(tier) {
|
||||
super(() => player.dimensions.antimatter, tier);
|
||||
|
@ -477,7 +475,10 @@ class AntimatterDimensionState extends DimensionState {
|
|||
// Nameless limits dim 8 purchases to 1 only
|
||||
// Continuum should be no different
|
||||
if (this.tier === 8 && Enslaved.isRunning) return 1;
|
||||
return this.costScale.getContinuumValue(Currency.antimatter.value, 10) * Laitela.matterExtraPurchaseFactor;
|
||||
// It's safe to use dimension.currencyAmount because this is
|
||||
// a dimension-only method (so don't just copy it over to tickspeed).
|
||||
// We need to use dimension.currencyAmount here because of different costs in NC6.
|
||||
return this.costScale.getContinuumValue(this.currencyAmount, 10) * Laitela.matterExtraPurchaseFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -524,7 +525,6 @@ class AntimatterDimensionState extends DimensionState {
|
|||
}
|
||||
|
||||
get isAvailableForPurchase() {
|
||||
if (Currency.antimatter.gt(Player.infinityLimit)) return false;
|
||||
if (!EternityMilestone.unlockAllND.isReached && this.tier > DimBoost.totalBoosts + 4) return false;
|
||||
const hasPrevTier = this.tier === 1 || AntimatterDimension(this.tier - 1).totalAmount.gt(0);
|
||||
if (!EternityMilestone.unlockAllND.isReached && !hasPrevTier) return false;
|
||||
|
|
|
@ -77,7 +77,7 @@ export function eternity(force, auto, specialConditions = {}) {
|
|||
if (!force) {
|
||||
if (!Player.canEternity) return false;
|
||||
EventHub.dispatch(GAME_EVENT.ETERNITY_RESET_BEFORE);
|
||||
if (!player.dilation.active) giveEternityRewards(auto);
|
||||
giveEternityRewards(auto);
|
||||
player.requirementChecks.reality.noEternities = false;
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@ export function eternity(force, auto, specialConditions = {}) {
|
|||
resetTickspeed();
|
||||
playerInfinityUpgradesOnReset();
|
||||
AchievementTimers.marathon2.reset();
|
||||
applyRealityUpgradesAfterEternity();
|
||||
applyEU1();
|
||||
player.records.thisInfinity.maxAM = DC.D0;
|
||||
player.records.thisEternity.maxAM = DC.D0;
|
||||
Currency.antimatter.reset();
|
||||
|
@ -137,22 +137,28 @@ export function eternity(force, auto, specialConditions = {}) {
|
|||
return true;
|
||||
}
|
||||
|
||||
export function animateAndEternity() {
|
||||
if (!Player.canEternity) return;
|
||||
// eslint-disable-next-line no-empty-function
|
||||
export function animateAndEternity(callback) {
|
||||
if (!Player.canEternity) return false;
|
||||
const hasAnimation = !FullScreenAnimationHandler.isDisplaying &&
|
||||
((player.dilation.active && player.options.animations.dilation) ||
|
||||
(!player.dilation.active && player.options.animations.eternity));
|
||||
|
||||
if (hasAnimation) {
|
||||
if (player.dilation.active) {
|
||||
animateAndUndilate();
|
||||
animateAndUndilate(callback);
|
||||
} else {
|
||||
eternityAnimation();
|
||||
setTimeout(eternity, 2250);
|
||||
setTimeout(() => {
|
||||
eternity();
|
||||
if (callback) callback();
|
||||
}, 2250);
|
||||
}
|
||||
} else {
|
||||
eternity();
|
||||
if (callback) callback();
|
||||
}
|
||||
return hasAnimation;
|
||||
}
|
||||
|
||||
export function initializeChallengeCompletions(isReality) {
|
||||
|
@ -189,12 +195,23 @@ export function initializeResourcesAfterEternity() {
|
|||
Player.resetRequirements("eternity");
|
||||
}
|
||||
|
||||
function applyRealityUpgradesAfterEternity() {
|
||||
export function applyEU1() {
|
||||
if (player.eternityUpgrades.size < 3 && Perk.autounlockEU1.canBeApplied) {
|
||||
for (const id of [1, 2, 3]) player.eternityUpgrades.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
// We want this to be checked before any EP-related autobuyers trigger, but we need to call this from the autobuyer
|
||||
// code since those run asynchronously from gameLoop
|
||||
export function applyEU2() {
|
||||
if (player.eternityUpgrades.size < 6 && Perk.autounlockEU2.canBeApplied) {
|
||||
const secondRow = EternityUpgrade.all.filter(u => u.id > 3);
|
||||
for (const upgrade of secondRow) {
|
||||
if (player.eternityPoints.gte(upgrade.cost / 1e10)) player.eternityUpgrades.add(upgrade.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function askEternityConfirmation() {
|
||||
if (player.dilation.active && player.options.confirmations.dilation) {
|
||||
Modal.exitDilation.show();
|
||||
|
|
|
@ -139,7 +139,7 @@ export class EternityChallengeState extends GameMechanicState {
|
|||
}
|
||||
|
||||
get isGoalReached() {
|
||||
return Currency.infinityPoints.gte(this.currentGoal);
|
||||
return player.records.thisEternity.maxIP.gte(this.currentGoal);
|
||||
}
|
||||
|
||||
get canBeCompleted() {
|
||||
|
@ -188,10 +188,11 @@ export class EternityChallengeState extends GameMechanicState {
|
|||
// If dilation is active, the { enteringEC: true } parameter will cause
|
||||
// dilation to not be disabled. We still don't force-eternity, though;
|
||||
// this causes TP to still be gained.
|
||||
const enteringGamespeed = getGameSpeedupFactor();
|
||||
if (Player.canEternity) eternity(false, auto, { enteringEC: true });
|
||||
player.challenge.eternity.current = this.id;
|
||||
if (this.id === 12) {
|
||||
if (player.requirementChecks.reality.slowestBH < 1) {
|
||||
if (enteringGamespeed < 0.001) {
|
||||
SecretAchievement(42).unlock();
|
||||
}
|
||||
player.requirementChecks.reality.slowestBH = 1;
|
||||
|
@ -310,7 +311,10 @@ export const EternityChallenges = {
|
|||
|
||||
autoComplete: {
|
||||
tick() {
|
||||
if (!player.reality.autoEC || Pelle.isDisabled("autoec")) return;
|
||||
if (!player.reality.autoEC || Pelle.isDisabled("autoec")) {
|
||||
player.reality.lastAutoEC = Math.clampMax(player.reality.lastAutoEC, this.interval);
|
||||
return;
|
||||
}
|
||||
if (Ra.unlocks.instantECAndRealityUpgradeAutobuyers.canBeApplied) {
|
||||
let next = this.nextChallenge;
|
||||
while (next !== undefined) {
|
||||
|
|
|
@ -136,7 +136,7 @@ export function manualRequestGalaxyReset(bulk) {
|
|||
if (!Galaxy.canBeBought || !Galaxy.requirement.isSatisfied) return;
|
||||
if (GameEnd.creditsEverClosed) return;
|
||||
if (player.options.confirmations.antimatterGalaxy) {
|
||||
Modal.antimatterGalaxy.show({ bulk });
|
||||
Modal.antimatterGalaxy.show({ bulk: bulk && EternityMilestone.autobuyMaxGalaxies.isReached });
|
||||
return;
|
||||
}
|
||||
requestGalaxyReset(bulk);
|
||||
|
|
|
@ -40,6 +40,9 @@ export const Glyphs = {
|
|||
get activeList() {
|
||||
return player.reality.glyphs.active;
|
||||
},
|
||||
get activeWithoutCompanion() {
|
||||
return this.activeList.filter(g => g.type !== "companion");
|
||||
},
|
||||
get allGlyphs() {
|
||||
return this.inventoryList.concat(this.activeList);
|
||||
},
|
||||
|
@ -292,7 +295,7 @@ export const Glyphs = {
|
|||
this.addToInventory(glyph, freeIndex, true);
|
||||
}
|
||||
this.updateRealityGlyphEffects();
|
||||
this.updateMaxGlyphCount();
|
||||
this.updateMaxGlyphCount(true);
|
||||
EventHub.dispatch(GAME_EVENT.GLYPHS_EQUIPPED_CHANGED);
|
||||
EventHub.dispatch(GAME_EVENT.GLYPHS_CHANGED);
|
||||
return !player.reality.glyphs.active.length;
|
||||
|
@ -305,7 +308,7 @@ export const Glyphs = {
|
|||
this.active[activeIndex] = null;
|
||||
this.addToInventory(glyph, requestedInventoryIndex, true);
|
||||
this.updateRealityGlyphEffects();
|
||||
this.updateMaxGlyphCount();
|
||||
this.updateMaxGlyphCount(true);
|
||||
EventHub.dispatch(GAME_EVENT.GLYPHS_EQUIPPED_CHANGED);
|
||||
EventHub.dispatch(GAME_EVENT.GLYPHS_CHANGED);
|
||||
},
|
||||
|
@ -445,11 +448,14 @@ export const Glyphs = {
|
|||
}
|
||||
if (player.reality.autoCollapse) this.collapseEmptySlots();
|
||||
},
|
||||
sortByLevel() {
|
||||
this.sort((a, b) => b.level - a.level);
|
||||
},
|
||||
sortByPower() {
|
||||
this.sort((a, b) => -a.level * a.strength + b.level * b.strength);
|
||||
this.sort((a, b) => b.level * b.strength - a.level * a.strength);
|
||||
},
|
||||
sortByScore() {
|
||||
this.sort((a, b) => -AutoGlyphProcessor.filterValue(a) + AutoGlyphProcessor.filterValue(b));
|
||||
this.sort((a, b) => AutoGlyphProcessor.filterValue(b) - AutoGlyphProcessor.filterValue(a));
|
||||
},
|
||||
sortByEffect() {
|
||||
function reverseBitstring(eff) {
|
||||
|
@ -457,7 +463,7 @@ export const Glyphs = {
|
|||
}
|
||||
// The bitwise reversal is so that the effects with the LOWER id are valued higher in the sorting.
|
||||
// This primarily meant for effarig glyph effect sorting, which makes it prioritize timespeed pow highest.
|
||||
this.sort((a, b) => -reverseBitstring(a.effects) + reverseBitstring(b.effects));
|
||||
this.sort((a, b) => reverseBitstring(b.effects) - reverseBitstring(a.effects));
|
||||
},
|
||||
// If there are enough glyphs that are better than the specified glyph, in every way, then
|
||||
// the glyph is objectively a useless piece of garbage.
|
||||
|
@ -540,6 +546,9 @@ export const Glyphs = {
|
|||
switch (player.reality.autoSort) {
|
||||
case AUTO_SORT_MODE.NONE:
|
||||
break;
|
||||
case AUTO_SORT_MODE.LEVEL:
|
||||
this.sortByLevel();
|
||||
break;
|
||||
case AUTO_SORT_MODE.POWER:
|
||||
this.sortByPower();
|
||||
break;
|
||||
|
@ -637,7 +646,7 @@ export const Glyphs = {
|
|||
// Normal glyph count minus 3 for each cursed glyph, uses 4 instead of 3 in the calculation because cursed glyphs
|
||||
// still contribute to the length of the active list. Note that it only ever decreases if startingReality is true.
|
||||
updateMaxGlyphCount(startingReality = false) {
|
||||
const activeGlyphList = this.activeList;
|
||||
const activeGlyphList = this.activeWithoutCompanion;
|
||||
const currCount = activeGlyphList.length - 4 * activeGlyphList.filter(x => x && x.type === "cursed").length;
|
||||
if (startingReality) player.requirementChecks.reality.maxGlyphs = currCount;
|
||||
player.requirementChecks.reality.maxGlyphs = Math.max(player.requirementChecks.reality.maxGlyphs, currCount);
|
||||
|
@ -664,7 +673,7 @@ export const Glyphs = {
|
|||
this.active[targetSlot] = glyph;
|
||||
glyph.idx = targetSlot;
|
||||
this.updateRealityGlyphEffects();
|
||||
this.updateMaxGlyphCount();
|
||||
this.updateMaxGlyphCount(true);
|
||||
EventHub.dispatch(GAME_EVENT.GLYPHS_EQUIPPED_CHANGED);
|
||||
EventHub.dispatch(GAME_EVENT.GLYPHS_CHANGED);
|
||||
this.validate();
|
||||
|
|
|
@ -321,9 +321,11 @@ GameKeyboard.bind(
|
|||
|
||||
// Toggle autobuyers
|
||||
function toggleAutobuyer(buyer) {
|
||||
// Autobuyer.tickspeed.isUnlocked is false without NC9, but we still want the simpler one to be togglable via hotkey
|
||||
const isSimpleTickspeed = buyer === Autobuyer.tickspeed && buyer.isBought;
|
||||
if (buyer.disabledByContinuum) {
|
||||
GameUI.notify.info("Continuum is enabled, you cannot alter this autobuyer");
|
||||
} else if (buyer.isUnlocked) {
|
||||
} else if (buyer.isUnlocked || isSimpleTickspeed) {
|
||||
buyer.toggle();
|
||||
GameUI.notify.info(`${buyer.name} Autobuyer toggled ${(buyer.isActive) ? "on" : "off"}`);
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ export const GameIntervals = (function() {
|
|||
),
|
||||
checkCloudSave: interval(() => {
|
||||
if (player.options.cloudEnabled && Cloud.loggedIn) Cloud.saveCheck();
|
||||
}, 300000),
|
||||
}, 600 * 1000),
|
||||
randomSecretAchievement: interval(() => {
|
||||
if (Math.random() < 0.00001) SecretAchievement(18).unlock();
|
||||
}, 1000),
|
||||
|
|
|
@ -514,6 +514,16 @@ window.logFactorial = (function() {
|
|||
};
|
||||
}());
|
||||
|
||||
window.exp1m = function(x) {
|
||||
if (x.abs().gte(0.001)) {
|
||||
return x.exp().minus(1);
|
||||
}
|
||||
// This sum contains all the terms that are relevant for |x| < 0.001. We could do some sort of loop
|
||||
// (add terms as long as they matter) but that probably has a greater fixed overhead, and we don't
|
||||
// call this enough for efficiency to be very worrying anyway.
|
||||
return x.plus(x.pow(2).div(2)).plus(x.pow(3).div(6)).plus(x.pow(4).div(24)).plus(x.pow(5).div(120));
|
||||
};
|
||||
|
||||
/** 32 bit XORSHIFT generator */
|
||||
window.xorshift32Update = function xorshift32Update(state) {
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
|
|
@ -18,17 +18,22 @@ export const NG = {
|
|||
const automatorConstants = JSON.stringify(player.reality.automator.constants);
|
||||
const automatorScripts = JSON.stringify(player.reality.automator.scripts);
|
||||
const fullCompletions = player.records.fullGameCompletions;
|
||||
const fullTimePlayed = player.records.previousRunRealTime + player.records.realTimePlayed;
|
||||
GlyphAppearanceHandler.unlockSet();
|
||||
const glyphCosmetics = JSON.stringify(player.reality.glyphs.cosmetics);
|
||||
Modal.hideAll();
|
||||
Quote.clearAll();
|
||||
GameStorage.hardReset();
|
||||
player.options = JSON.parse(backUpOptions);
|
||||
// We need to force this one to be true because otherwise the player will be unable to select their glyphs
|
||||
// until they can auto-reality
|
||||
player.options.confirmations.glyphSelection = true;
|
||||
player.secretUnlocks = secretUnlocks;
|
||||
player.secretAchievementBits = JSON.parse(secretAchievements);
|
||||
player.reality.automator.constants = JSON.parse(automatorConstants);
|
||||
player.reality.automator.scripts = JSON.parse(automatorScripts);
|
||||
player.records.fullGameCompletions = fullCompletions + 1;
|
||||
player.records.previousRunRealTime = fullTimePlayed;
|
||||
ui.view.newUI = player.options.newUI;
|
||||
ui.view.news = player.options.news.enabled;
|
||||
player.reality.glyphs.cosmetics = JSON.parse(glyphCosmetics);
|
||||
|
|
|
@ -23,6 +23,7 @@ export const NewsHandler = {
|
|||
// we pad the array with zeroes until we can fit the new ID in before actually adding it.
|
||||
while (this.BITS_PER_MASK * player.news.seen[type].length <= number) player.news.seen[type].push(0);
|
||||
player.news.seen[type][Math.floor(number / this.BITS_PER_MASK)] |= 1 << (number % this.BITS_PER_MASK);
|
||||
player.news.totalSeen++;
|
||||
},
|
||||
|
||||
hasSeenNews(id) {
|
||||
|
|
|
@ -47,6 +47,13 @@ class PerkState extends SetPurchasableMechanicState {
|
|||
|
||||
onPurchased() {
|
||||
if (this.config.bumpCurrency !== undefined) this.config.bumpCurrency();
|
||||
if (this.label === "EU1" && Currency.eternities.gt(0)) applyEU1();
|
||||
if (this.label === "ACHNR") {
|
||||
if (Achievements.preReality.some(a => !a.isUnlocked)) player.reality.gainedAutoAchievements = true;
|
||||
for (const achievement of Achievements.preReality) {
|
||||
achievement.unlock(true);
|
||||
}
|
||||
}
|
||||
GameCache.achievementPeriod.invalidate();
|
||||
GameCache.buyablePerks.invalidate();
|
||||
EventHub.dispatch(GAME_EVENT.PERK_BOUGHT);
|
||||
|
|
|
@ -62,6 +62,8 @@ window.player = {
|
|||
mode: 0,
|
||||
rm: DC.D1,
|
||||
glyph: 0,
|
||||
time: 0,
|
||||
shard: 0,
|
||||
isActive: false
|
||||
},
|
||||
eternity: {
|
||||
|
@ -271,19 +273,21 @@ window.player = {
|
|||
realTimePlayed: 0,
|
||||
realTimeDoomed: 0,
|
||||
fullGameCompletions: 0,
|
||||
previousRunRealTime: 0,
|
||||
totalAntimatter: DC.E1,
|
||||
lastTenInfinities: Array.range(0, 10).map(() =>
|
||||
[Number.MAX_VALUE, DC.D1, DC.D1, Number.MAX_VALUE]),
|
||||
lastTenEternities: Array.range(0, 10).map(() =>
|
||||
[Number.MAX_VALUE, DC.D1, DC.D1, Number.MAX_VALUE]),
|
||||
lastTenRealities: Array.range(0, 10).map(() =>
|
||||
[Number.MAX_VALUE, DC.D1, 1, Number.MAX_VALUE, 0]),
|
||||
recentInfinities: Array.range(0, 10).map(() =>
|
||||
[Number.MAX_VALUE, Number.MAX_VALUE, DC.D1, DC.D1, ""]),
|
||||
recentEternities: Array.range(0, 10).map(() =>
|
||||
[Number.MAX_VALUE, Number.MAX_VALUE, DC.D1, DC.D1, "", DC.D0]),
|
||||
recentRealities: Array.range(0, 10).map(() =>
|
||||
[Number.MAX_VALUE, Number.MAX_VALUE, DC.D1, 1, "", 0, 0]),
|
||||
thisInfinity: {
|
||||
time: 0,
|
||||
realTime: 0,
|
||||
lastBuyTime: 0,
|
||||
maxAM: DC.D0,
|
||||
bestIPmin: DC.D0,
|
||||
bestIPminVal: DC.D0,
|
||||
},
|
||||
bestInfinity: {
|
||||
time: Number.MAX_VALUE,
|
||||
|
@ -298,6 +302,7 @@ window.player = {
|
|||
maxIP: DC.D0,
|
||||
bestIPMsWithoutMaxAll: DC.D0,
|
||||
bestEPmin: DC.D0,
|
||||
bestEPminVal: DC.D0,
|
||||
bestInfinitiesPerMs: DC.D0,
|
||||
},
|
||||
bestEternity: {
|
||||
|
@ -314,6 +319,8 @@ window.player = {
|
|||
bestEternitiesPerMs: DC.D0,
|
||||
maxReplicanti: DC.D0,
|
||||
maxDT: DC.D0,
|
||||
bestRSmin: 0,
|
||||
bestRSminVal: 0,
|
||||
},
|
||||
bestReality: {
|
||||
time: Number.MAX_VALUE,
|
||||
|
@ -521,7 +528,7 @@ window.player = {
|
|||
},
|
||||
constants: {},
|
||||
execTimer: 0,
|
||||
type: AUTOMATOR_TYPE.BLOCK,
|
||||
type: AUTOMATOR_TYPE.TEXT,
|
||||
forceUnlock: false,
|
||||
currentInfoPane: AutomatorPanels.INTRO_PAGE,
|
||||
},
|
||||
|
@ -584,7 +591,6 @@ window.player = {
|
|||
storedReal: 0,
|
||||
autoStoreReal: false,
|
||||
isAutoReleasing: false,
|
||||
storedFraction: 1,
|
||||
quoteBits: 0,
|
||||
unlocks: [],
|
||||
run: false,
|
||||
|
@ -779,6 +785,7 @@ window.player = {
|
|||
retryCelestial: false,
|
||||
showAllChallenges: false,
|
||||
cloudEnabled: true,
|
||||
hideGoogleName: false,
|
||||
showCloudModal: true,
|
||||
forceCloudOverwrite: false,
|
||||
syncSaveIntervals: true,
|
||||
|
@ -791,8 +798,8 @@ window.player = {
|
|||
offlineProgress: true,
|
||||
automaticTabSwitching: true,
|
||||
respecIntoProtected: false,
|
||||
offlineTicks: 1000,
|
||||
showLastTenResourceGain: true,
|
||||
offlineTicks: 1e5,
|
||||
showRecentRate: true,
|
||||
autosaveInterval: 30000,
|
||||
showTimeSinceSave: true,
|
||||
saveFileName: "",
|
||||
|
@ -888,7 +895,8 @@ window.player = {
|
|||
maxEntries: 200,
|
||||
clearOnReality: true,
|
||||
clearOnRestart: true,
|
||||
}
|
||||
},
|
||||
invertTTgenDisplay: false,
|
||||
},
|
||||
IAP: {
|
||||
enabled: false,
|
||||
|
|
|
@ -340,6 +340,30 @@ export function beginProcessReality(realityProps) {
|
|||
// Do this before processing glyphs so that we don't try to reality again while async is running.
|
||||
finishProcessReality(realityProps);
|
||||
|
||||
// If we have less than a certain amount of simulated realities, then we just shortcut the heavier async and
|
||||
// sampling code in order to just directly give all the glyphs. The later code is a fixed amount of overhead
|
||||
// which is large enough that quick realities can cause it to softlock the game due to lag on slower devices
|
||||
// Note: This is mostly a copy-paste of a code block in processManualReality() with slight modifications
|
||||
if (glyphsToProcess < 100) {
|
||||
for (let glyphNum = 0; glyphNum < glyphsToProcess; glyphNum++) {
|
||||
if (EffarigUnlock.glyphFilter.isUnlocked) {
|
||||
const glyphChoices = GlyphSelection.glyphList(GlyphSelection.choiceCount,
|
||||
realityProps.gainedGlyphLevel, { rng });
|
||||
const newGlyph = AutoGlyphProcessor.pick(glyphChoices);
|
||||
if (!AutoGlyphProcessor.wouldKeep(newGlyph) || GameCache.glyphInventorySpace.value === 0) {
|
||||
AutoGlyphProcessor.getRidOfGlyph(newGlyph);
|
||||
} else {
|
||||
Glyphs.addToInventory(newGlyph);
|
||||
}
|
||||
} else {
|
||||
GlyphSelection.select(Math.floor(Math.random() * GlyphSelection.choiceCount), false);
|
||||
}
|
||||
}
|
||||
rng.finalize();
|
||||
Glyphs.processSortingAfterReality();
|
||||
return;
|
||||
}
|
||||
|
||||
// We need these variables in this scope in order to modify the behavior of the Async loop while it's running
|
||||
const progress = {};
|
||||
let fastToggle = false;
|
||||
|
@ -409,7 +433,7 @@ export function beginProcessReality(realityProps) {
|
|||
if (VUnlocks.autoAutoClean.canBeApplied && player.reality.autoAutoClean) Glyphs.autoClean();
|
||||
}
|
||||
};
|
||||
const glyphsToSample = 10000;
|
||||
const glyphsToSample = Math.min(glyphsToProcess, 10000);
|
||||
Async.run(glyphFunction,
|
||||
glyphsToProcess,
|
||||
{
|
||||
|
@ -669,6 +693,10 @@ export function finishProcessReality(realityProps) {
|
|||
|
||||
if (TeresaUnlocks.startEU.canBeApplied) {
|
||||
for (const id of [1, 2, 3, 4, 5, 6]) player.eternityUpgrades.add(id);
|
||||
} else if (RealityUpgrade(14).isBought) {
|
||||
// Eternal flow will always give eternities after the first tick,
|
||||
// better to try apply EU1 immediately once at the start rather than on every tick
|
||||
applyEU1();
|
||||
}
|
||||
|
||||
if (!isReset) Ra.applyAlchemyReactions(realityRealTime);
|
||||
|
@ -729,6 +757,8 @@ export function applyRUPG10() {
|
|||
Currency.eternities.bumpTo(100);
|
||||
Replicanti.amount = Replicanti.amount.clampMin(1);
|
||||
Replicanti.unlock(true);
|
||||
|
||||
applyEU1();
|
||||
}
|
||||
|
||||
export function clearCelestialRuns() {
|
||||
|
|
|
@ -7,6 +7,7 @@ export const ReplicantiGrowth = {
|
|||
return Math.log10(Number.MAX_VALUE);
|
||||
},
|
||||
get scaleFactor() {
|
||||
if (PelleStrikes.eternity.hasStrike && Replicanti.amount.gte(DC.E2000)) return 10;
|
||||
if (Pelle.isDoomed) return 2;
|
||||
return AlchemyResource.cardinality.effectValue;
|
||||
}
|
||||
|
@ -89,12 +90,13 @@ export function getReplicantiInterval(overCapOverride, intervalIn) {
|
|||
}
|
||||
|
||||
if (overCap) {
|
||||
const increases = (amount.log10() - replicantiCap().log10()) / ReplicantiGrowth.scaleLog10;
|
||||
interval = interval.times(Decimal.pow(ReplicantiGrowth.scaleFactor, increases));
|
||||
if (PelleStrikes.eternity.hasStrike && amount.e > 2000) {
|
||||
const pelleIncreases = (amount.log10() - 2000) / ReplicantiGrowth.scaleLog10;
|
||||
interval = interval.times(Decimal.pow(5, pelleIncreases));
|
||||
let increases = (amount.log10() - replicantiCap().log10()) / ReplicantiGrowth.scaleLog10;
|
||||
if (PelleStrikes.eternity.hasStrike && amount.gte(DC.E2000)) {
|
||||
// The above code assumes in this case there's 10x scaling for every 1e308 increase;
|
||||
// in fact, before e2000 it's only 2x.
|
||||
increases -= Math.log10(5) * (2000 - replicantiCap().log10()) / ReplicantiGrowth.scaleLog10;
|
||||
}
|
||||
interval = interval.times(Decimal.pow(ReplicantiGrowth.scaleFactor, increases));
|
||||
}
|
||||
|
||||
interval = interval.div(PelleRifts.decay.effectValue);
|
||||
|
@ -180,6 +182,10 @@ export function replicantiLoop(diff) {
|
|||
if (isUncapped && Replicanti.amount.gte(replicantiCap()) && remainingGain.gt(0)) {
|
||||
// Recalculate the interval (it may have increased due to additional replicanti, or,
|
||||
// far less importantly, decreased due to Reality Upgrade 6 and additional RG).
|
||||
// Don't worry here about the lack of e2000 scaling in Pelle on the first tick
|
||||
// (with replicanti still under e2000) causing a huge replicanti jump;
|
||||
// there's code later to stop replicanti from increasing by more than e308
|
||||
// in a single tick in Pelle.
|
||||
const intervalRatio = getReplicantiInterval(true).div(interval);
|
||||
remainingGain = remainingGain.div(intervalRatio);
|
||||
Replicanti.amount =
|
||||
|
|
|
@ -377,10 +377,11 @@ GameDatabase.achievements.normal = [
|
|||
name: "Bulked Up",
|
||||
get description() {
|
||||
return `Get all of your Antimatter Dimension Autobuyer bulk amounts to
|
||||
${formatInt(Autobuyer.antimatterDimension.bulkCap)} or higher.`;
|
||||
${formatInt(Autobuyer.antimatterDimension.bulkCap)}.`;
|
||||
},
|
||||
checkRequirement: () => Autobuyer.antimatterDimension.zeroIndexed.every(x => x.hasMaxedBulk),
|
||||
checkEvent: [GAME_EVENT.REALITY_RESET_AFTER, GAME_EVENT.REALITY_UPGRADE_TEN_BOUGHT],
|
||||
checkEvent: [GAME_EVENT.REALITY_RESET_AFTER, GAME_EVENT.REALITY_UPGRADE_TEN_BOUGHT,
|
||||
GAME_EVENT.SAVE_CONVERTED_FROM_PREVIOUS_VERSION],
|
||||
reward: "Dimension Autobuyer bulks are unlimited."
|
||||
},
|
||||
{
|
||||
|
@ -712,7 +713,7 @@ GameDatabase.achievements.normal = [
|
|||
checkRequirement: () => Currency.infinityPoints.exponent >= 1000,
|
||||
checkEvent: GAME_EVENT.GAME_TICK_AFTER,
|
||||
get reward() {
|
||||
return `Make the Infinity Point formula better. log(x/${formatInt(308)}) ➜ log(x/${formatFloat(307.8, 1)})`;
|
||||
return `Make the Infinity Point formula better. log(x)/${formatInt(308)} ➜ log(x)/${formatFloat(307.8, 1)}`;
|
||||
},
|
||||
effect: 307.8
|
||||
},
|
||||
|
@ -764,8 +765,8 @@ GameDatabase.achievements.normal = [
|
|||
${format(Decimal.NUMBER_MAX_VALUE, 1, 0)} times higher Infinity Points than the previous one.`;
|
||||
},
|
||||
checkRequirement: () => {
|
||||
if (player.records.lastTenInfinities.some(i => i[0] === Number.MAX_VALUE)) return false;
|
||||
const infinities = player.records.lastTenInfinities.map(run => run[1]);
|
||||
if (player.records.recentInfinities.some(i => i[0] === Number.MAX_VALUE)) return false;
|
||||
const infinities = player.records.recentInfinities.map(run => run[2]);
|
||||
for (let i = 0; i < infinities.length - 1; i++) {
|
||||
if (infinities[i].lt(infinities[i + 1].times(Decimal.NUMBER_MAX_VALUE))) return false;
|
||||
}
|
||||
|
@ -932,8 +933,8 @@ GameDatabase.achievements.normal = [
|
|||
id: 132,
|
||||
name: "Unique snowflakes",
|
||||
get description() {
|
||||
return `Have ${formatInt(569)} Antimatter Galaxies without getting any
|
||||
Replicanti Galaxies in your current Eternity.`;
|
||||
return `Have ${formatInt(569)} Antimatter Galaxies without gaining any
|
||||
Replicanti Galaxies in your current Eternity.`;
|
||||
},
|
||||
checkRequirement: () => player.galaxies >= 569 && player.requirementChecks.eternity.noRG,
|
||||
checkEvent: GAME_EVENT.GALAXY_RESET_AFTER,
|
||||
|
@ -1040,8 +1041,8 @@ GameDatabase.achievements.normal = [
|
|||
${format(Decimal.NUMBER_MAX_VALUE, 1, 0)} times higher Eternity Points than the previous one.`;
|
||||
},
|
||||
checkRequirement: () => {
|
||||
if (player.records.lastTenEternities.some(i => i[0] === Number.MAX_VALUE)) return false;
|
||||
const eternities = player.records.lastTenEternities.map(run => run[1]);
|
||||
if (player.records.recentEternities.some(i => i[0] === Number.MAX_VALUE)) return false;
|
||||
const eternities = player.records.recentEternities.map(run => run[2]);
|
||||
for (let i = 0; i < eternities.length - 1; i++) {
|
||||
if (eternities[i].lt(eternities[i + 1].times(Decimal.NUMBER_MAX_VALUE))) return false;
|
||||
}
|
||||
|
@ -1089,7 +1090,7 @@ GameDatabase.achievements.normal = [
|
|||
.every(type => Glyphs.activeList.some(g => g.type === type)),
|
||||
checkEvent: GAME_EVENT.REALITY_RESET_BEFORE,
|
||||
reward: "Gained Glyph level is increased by number of distinct Glyph types equipped.",
|
||||
effect: () => (new Set(Glyphs.activeList.map(g => g.type))).size,
|
||||
effect: () => (new Set(Glyphs.activeWithoutCompanion.map(g => g.type))).size,
|
||||
formatEffect: value => `+${formatInt(value)}`
|
||||
},
|
||||
{
|
||||
|
@ -1141,7 +1142,7 @@ GameDatabase.achievements.normal = [
|
|||
description: "Reality without buying Time Theorems.",
|
||||
checkRequirement: () => player.requirementChecks.reality.noPurchasedTT,
|
||||
checkEvent: GAME_EVENT.REALITY_RESET_BEFORE,
|
||||
get reward() { return `Gain ${formatX(2.5, 0, 1)} Time Theorems, and a free coupon to McDonalds™️.`; },
|
||||
get reward() { return `Gain ${formatX(2.5, 0, 1)} generated Time Theorems, and a free coupon to McDonalds™️.`; },
|
||||
effect: 2.5
|
||||
},
|
||||
{
|
||||
|
@ -1252,7 +1253,7 @@ GameDatabase.achievements.normal = [
|
|||
any Charged Infinity Upgrades, having any equipped Glyphs, or buying any Triad Studies.`;
|
||||
},
|
||||
checkRequirement: () => MachineHandler.gainedRealityMachines.gte(Decimal.NUMBER_MAX_VALUE) &&
|
||||
player.celestials.ra.charged.size === 0 && Glyphs.activeList.length === 0 &&
|
||||
player.celestials.ra.charged.size === 0 && Glyphs.activeWithoutCompanion.length === 0 &&
|
||||
player.requirementChecks.reality.noTriads,
|
||||
checkEvent: GAME_EVENT.REALITY_RESET_BEFORE,
|
||||
},
|
||||
|
|
|
@ -115,7 +115,7 @@ GameDatabase.achievements.secret = [
|
|||
checkRequirement: () =>
|
||||
Time.bestInfinity.totalMilliseconds <= 1 ||
|
||||
Time.bestEternity.totalMilliseconds <= 1,
|
||||
checkEvent: [GAME_EVENT.BIG_CRUNCH_BEFORE, GAME_EVENT.ETERNITY_RESET_BEFORE]
|
||||
checkEvent: [GAME_EVENT.BIG_CRUNCH_AFTER, GAME_EVENT.ETERNITY_RESET_AFTER]
|
||||
},
|
||||
{
|
||||
id: 33,
|
||||
|
|
|
@ -97,6 +97,7 @@ GameDatabase.awayProgressTypes = [
|
|||
showOption: false,
|
||||
}, {
|
||||
name: "enslavedMemories",
|
||||
forcedName: "Nameless Memories",
|
||||
awayOption: "celestialMemories",
|
||||
reference: ["celestials", "ra", "pets", "enslaved", "memories"],
|
||||
isUnlocked: () => Ra.pets.enslaved.isUnlocked && !Ra.pets.enslaved.isCapped,
|
||||
|
|
|
@ -104,7 +104,7 @@ GameDatabase.catchupResources = [
|
|||
id: 13,
|
||||
openH2pEntry: "Eternity",
|
||||
requiredStage: PROGRESS_STAGE.EARLY_ETERNITY,
|
||||
description: `Infinity Points are the primary resource after completing your first Eternity, and scale based on your
|
||||
description: `Eternity Points are the primary resource after completing your first Eternity, and scale based on your
|
||||
Infinity Points at the time you complete the Eternity.`
|
||||
},
|
||||
{
|
||||
|
|
|
@ -235,7 +235,7 @@ GameDatabase.celestials.alchemy.resources = {
|
|||
unlockedAt: 15,
|
||||
description: "provides a power to all Dimensions that permanently grows over time",
|
||||
formatEffect: value => `All Dimensions ${formatPow(Ra.momentumValue, 4, 4)}, increasing by
|
||||
${format(0.002 * Achievement(175).effectOrDefault(1), 3, 3)}
|
||||
${format(0.005 * Achievement(175).effectOrDefault(1), 3, 3)}
|
||||
per real-time hour after the resource is unlocked, up to a maximum of ${formatPow(value, 4, 4)}`,
|
||||
reagents: [
|
||||
{
|
||||
|
|
|
@ -7,7 +7,7 @@ GameDatabase.celestials.enslaved = {
|
|||
id: 0,
|
||||
hint: "The Nameless Ones want to help, but the help takes a while.",
|
||||
condition: () => `Spent more than ${formatInt(5)} real-time hours inside the Reality without completing it;
|
||||
time outside the Reality counts for ${formatPercents(0.04)} as much. The timer starts once Nameless's
|
||||
time outside the Reality counts for ${formatPercents(0.4)} as much. The timer starts once the
|
||||
Reality is unlocked, but accumulates continuously.`,
|
||||
},
|
||||
ec1: {
|
||||
|
|
|
@ -140,7 +140,7 @@ function pelleRiftFill(name, index, textAngle, fillType) {
|
|||
visibleCheck = () => riftFillStage(name) === FILL_STATE.FILL;
|
||||
progressFn = () => Math.clamp(0.1 + PelleRifts[name.toLowerCase()].realPercentage / 0.9, 1e-6, 1);
|
||||
legendFn = () => false;
|
||||
percentFn = x => (x - 0.1) / 0.9;
|
||||
percentFn = () => PelleRifts[name.toLowerCase()].realPercentage;
|
||||
incompleteClass = "c-celestial-nav__test-incomplete";
|
||||
nodeFill = "crimson";
|
||||
connectorFill = "crimson";
|
||||
|
@ -153,7 +153,7 @@ function pelleRiftFill(name, index, textAngle, fillType) {
|
|||
visibleCheck = () => riftFillStage(name) >= FILL_STATE.DRAIN;
|
||||
progressFn = () => Math.clamp(Math.sqrt(PelleRifts[name.toLowerCase()].reducedTo), 1e-6, 1);
|
||||
legendFn = () => riftFillStage(name) === FILL_STATE.DRAIN && PelleRifts[name.toLowerCase()].reducedTo < 1;
|
||||
percentFn = x => x;
|
||||
percentFn = () => PelleRifts[name.toLowerCase()].reducedTo;
|
||||
incompleteClass = "c-celestial-nav__drained-rift";
|
||||
nodeFill = "crimson";
|
||||
connectorFill = "#550919";
|
||||
|
@ -161,7 +161,7 @@ function pelleRiftFill(name, index, textAngle, fillType) {
|
|||
case FILL_STATE.OVERFILL:
|
||||
visibleCheck = () => riftFillStage(name) === FILL_STATE.OVERFILL;
|
||||
progressFn = () => Math.clamp(PelleRifts[name.toLowerCase()].percentage - 1, 1e-6, 1);
|
||||
percentFn = x => x + 1;
|
||||
percentFn = () => PelleRifts[name.toLowerCase()].percentage;
|
||||
legendFn = () => true;
|
||||
incompleteClass = undefined;
|
||||
nodeFill = "#ff7700";
|
||||
|
@ -182,8 +182,8 @@ function pelleRiftFill(name, index, textAngle, fillType) {
|
|||
},
|
||||
forceLegend: () => legendFn(),
|
||||
legend: {
|
||||
text: complete => [
|
||||
`${formatPercents(percentFn(complete), 1)} ${wordShift.wordCycle(PelleRifts[name.toLowerCase()].name)}`
|
||||
text: () => [
|
||||
`${formatPercents(percentFn(), 1)} ${wordShift.wordCycle(PelleRifts[name.toLowerCase()].name)}`
|
||||
],
|
||||
angle: textAngle,
|
||||
diagonal: 30,
|
||||
|
@ -586,7 +586,7 @@ GameDatabase.celestials.navigation = {
|
|||
},
|
||||
legend: {
|
||||
text: complete => {
|
||||
if (complete >= 1) return "Broken the chain with Glyph level";
|
||||
if (complete >= 1) return "Glyph level chain has been broken";
|
||||
const goal = 5000;
|
||||
return [
|
||||
"Break a chain",
|
||||
|
@ -631,7 +631,7 @@ GameDatabase.celestials.navigation = {
|
|||
},
|
||||
legend: {
|
||||
text: complete => {
|
||||
if (complete >= 1) return "Broken the chain with Glyph rarity";
|
||||
if (complete >= 1) return "Glyph rarity chain has been broken";
|
||||
const goal = 100;
|
||||
return [
|
||||
"Break a chain",
|
||||
|
@ -1527,8 +1527,8 @@ GameDatabase.celestials.navigation = {
|
|||
if (upgrade.isAvailableForPurchase) return [
|
||||
dmdText,
|
||||
`Imaginary Machines
|
||||
${format(Math.min(upgrade.currency.value, upgrade.cost), upgrade.canBeBought ? 0 : 2)}
|
||||
/ ${format(upgrade.cost)}`
|
||||
${format(Math.min(upgrade.currency.value, upgrade.cost), upgrade.canBeBought ? 1 : 2)}
|
||||
/ ${format(upgrade.cost, 1)}`
|
||||
];
|
||||
|
||||
if (player.celestials.laitela.fastestCompletion > 30 && Laitela.difficultyTier < 0) return [
|
||||
|
@ -1814,7 +1814,7 @@ GameDatabase.celestials.navigation = {
|
|||
if (Pelle.isUnlocked) return 1;
|
||||
const imCost = Math.clampMax(emphasizeEnd(Math.log10(Currency.imaginaryMachines.value) / Math.log10(1.6e15)), 1);
|
||||
let laitelaProgress = Laitela.isRunning ? Math.min(Currency.eternityPoints.value.log10() / 4000, 0.99) : 0;
|
||||
if (Laitela.difficultyTier !== 8 || Glyphs.activeList.length > 1) laitelaProgress = 0;
|
||||
if (Laitela.difficultyTier !== 8 || Glyphs.activeWithoutCompanion.length > 1) laitelaProgress = 0;
|
||||
else if (ImaginaryUpgrade(25).isAvailableForPurchase) laitelaProgress = 1;
|
||||
return (imCost + laitelaProgress) / 2;
|
||||
},
|
||||
|
@ -1835,7 +1835,7 @@ GameDatabase.celestials.navigation = {
|
|||
];
|
||||
}
|
||||
let laitelaString = `${format(Currency.eternityPoints.value)} / ${format("1e4000")} EP`;
|
||||
if (!Laitela.isRunning || Laitela.difficultyTier !== 8 || Glyphs.activeList.length > 1) {
|
||||
if (!Laitela.isRunning || Laitela.difficultyTier !== 8 || Glyphs.activeWithoutCompanion.length > 1) {
|
||||
laitelaString = "Lai'tela's Reality is still intact";
|
||||
} else if (ImaginaryUpgrade(25).isAvailableForPurchase) {
|
||||
laitelaString = "Lai'tela's Reality has been destroyed";
|
||||
|
|
|
@ -184,10 +184,10 @@ GameDatabase.celestials.ra = {
|
|||
level: 5,
|
||||
displayIcon: `<span class="fas fa-stopwatch"></span>`
|
||||
},
|
||||
adjustableStoredTime: {
|
||||
autoPulseTime: {
|
||||
id: 17,
|
||||
reward: () => `Black Hole charging can be done at an adjustable rate and automatically
|
||||
pulsed every ${formatInt(5)} ticks. You can change these in the Black Hole and The Nameless Ones' tabs`,
|
||||
reward: () => `Black Hole charging now only uses ${formatPercents(0.99)} of your game speed and you can
|
||||
automatically discharge ${formatPercents(0.01)} of your stored game time every ${formatInt(5)} ticks.`,
|
||||
pet: "enslaved",
|
||||
level: 10,
|
||||
displayIcon: `<span class="fas fa-expand-arrows-alt"></span>`,
|
||||
|
|
|
@ -174,7 +174,7 @@ GameDatabase.celestials.pelle.rifts = {
|
|||
{
|
||||
resource: "recursion",
|
||||
requirement: 1,
|
||||
description: "Unlock the Galaxy Generator",
|
||||
description: "Permanently unlock the Galaxy Generator",
|
||||
},
|
||||
],
|
||||
galaxyGeneratorText: "Creating more Galaxies is unsustainable, you must focus the $value to allow more"
|
||||
|
|
|
@ -282,7 +282,7 @@ GameDatabase.celestials.singularityMilestones = {
|
|||
upgradeDirection: LAITELA_UPGRADE_DIRECTION.BOOSTS_MAIN,
|
||||
},
|
||||
tesseractMultFromSingularities: {
|
||||
start: 8e45,
|
||||
start: 2.5e45,
|
||||
repeat: 0,
|
||||
limit: 1,
|
||||
description: "Singularities increase effective Tesseract count",
|
||||
|
|
|
@ -37,7 +37,7 @@ GameDatabase.celestials.pelle.strikes = {
|
|||
id: 5,
|
||||
requirementDescription: "Dilate Time",
|
||||
penaltyDescription: "Time Dilation is permanently active",
|
||||
rewardDescription: () => `Keep access to Time Dilation upgrades across Armageddon and unlock
|
||||
rewardDescription: () => `Keep the Time Dilation study across Armageddon, boost Remnant gain, and unlock
|
||||
${wordShift.wordCycle(PelleRifts.paradox.name)}`,
|
||||
rift: () => PelleRifts.paradox
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ GameDatabase.celestials.v = {
|
|||
// This achievement has internally negated values since the check is always greater than
|
||||
values: [-5, -4, -3, -2, -1, 0],
|
||||
condition: () => V.isRunning && TimeStudy.reality.isBought,
|
||||
currentValue: () => -Glyphs.activeList.length,
|
||||
currentValue: () => -Glyphs.activeWithoutCompanion.length,
|
||||
formatRecord: x => (x >= -5 ? formatInt(-x) : "Not reached"),
|
||||
shardReduction: () => 0,
|
||||
maxShardReduction: () => 0,
|
||||
|
@ -230,7 +230,7 @@ GameDatabase.celestials.v = {
|
|||
},
|
||||
autoAutoClean: {
|
||||
id: 4,
|
||||
reward: "Unlock the ability to Automatically Purge on Reality.",
|
||||
reward: "Unlock the ability to Automatically Purge Glyphs on Reality.",
|
||||
description: () => `Have ${formatInt(16)} V-Achievements`,
|
||||
requirement: () => V.spaceTheorems >= 16
|
||||
},
|
||||
|
|
|
@ -168,7 +168,7 @@ GameDatabase.challenges.eternity = [
|
|||
id: 11,
|
||||
description: () => `all Dimension multipliers and powers are disabled except for the multipliers from
|
||||
Infinity Power and Dimension Boosts (to Antimatter Dimensions). ${specialInfinityGlyphDisabledEffectText()}`,
|
||||
goal: DC.E500,
|
||||
goal: DC.E450,
|
||||
pelleGoal: DC.E11200,
|
||||
goalIncrease: DC.E200,
|
||||
pelleGoalIncrease: DC.E1400,
|
||||
|
|
|
@ -38,8 +38,7 @@ GameDatabase.challenges.infinity = [
|
|||
effect: () => Decimal.pow(1.05 + (player.galaxies * 0.005), player.totalTickBought),
|
||||
formatEffect: value => formatX(value, 2, 2),
|
||||
reward: {
|
||||
description: `Antimatter Dimension multiplier based on Antimatter Galaxies and Tickspeed purchases
|
||||
(same as challenge multiplier)`,
|
||||
description: `Antimatter Dimension multiplier based on Antimatter Galaxies and Tickspeed purchases`,
|
||||
effect: () => (Laitela.continuumActive
|
||||
? Decimal.pow(1.05 + (player.galaxies * 0.005), Tickspeed.continuumValue)
|
||||
: Decimal.pow(1.05 + (player.galaxies * 0.005), player.totalTickBought)),
|
||||
|
@ -64,8 +63,8 @@ GameDatabase.challenges.infinity = [
|
|||
{
|
||||
id: 5,
|
||||
description:
|
||||
`buying Antimatter Dimensions 1-4 causes all smaller Antimatter Dimension costs to increase.
|
||||
Buying Antimatter Dimensions 5-8 causes all larger Antimatter Dimension costs to increase.`,
|
||||
`buying Antimatter Dimensions 1-4 causes all cheaper AD costs to increase.
|
||||
Buying Antimatter Dimensions 5-8 causes all more expensive AD costs to increase.`,
|
||||
goal: DC.E16500,
|
||||
isQuickResettable: true,
|
||||
reward: {
|
||||
|
@ -105,7 +104,7 @@ GameDatabase.challenges.infinity = [
|
|||
TimeStudy(81)
|
||||
);
|
||||
return `you cannot buy Antimatter Galaxies. Base Dimension Boost multiplier is increased to a maximum
|
||||
of ${formatX(10)}. (Current base multiplier: ${formatX(mult)})`;
|
||||
of ${formatX(10)}. (Current base multiplier: ${formatX(mult, 2, 1)})`;
|
||||
},
|
||||
goal: DC.E10000,
|
||||
isQuickResettable: false,
|
||||
|
|
|
@ -128,8 +128,8 @@ GameDatabase.challenges.normal = [
|
|||
legacyId: 7,
|
||||
isQuickResettable: false,
|
||||
description: () => `each Antimatter Dimension produces the Dimension ${formatInt(2)} tiers below it
|
||||
instead of ${formatInt(1)}. The 1st Dimension still produces antimatter, and the 2nd, 4th, and 6th
|
||||
Dimensions are made stronger to compensate.`,
|
||||
instead of ${formatInt(1)}. Both 1st and 2nd Dimensions produce antimatter.
|
||||
The 2nd, 4th, and 6th Dimensions are made stronger to compensate.`,
|
||||
name: "Automated Big Crunches",
|
||||
reward: "Big Crunches Autobuyer",
|
||||
lockedAt: DC.D16,
|
||||
|
|
|
@ -28,7 +28,7 @@ GameDatabase.changelog = [
|
|||
<li>Added a How to Play modal with much more detail compared to the old How to Play.</li>
|
||||
<li>Added 5 new rows of achievements.</li>
|
||||
<li>Added a Multiplier Breakdown subtab.</li>
|
||||
<li>Added more Nicholas Cage.</li>
|
||||
<li>Added more Nicolas Cage.</li>
|
||||
<li>Cloud saving is now available to everyone. This needs your Google account.</li>
|
||||
<li>Shop tab is now available to everyone.</li>
|
||||
<li>Redesigned overall UI styling.</li>
|
||||
|
@ -136,6 +136,7 @@ Balance Changes:
|
|||
<li>The 20-eternities milestone was moved to 8-eternities.</li>
|
||||
<li>Increased cost scaling for Time Dimensions after 1e6000.</li>
|
||||
<li>TS 83 has been hard capped.</li>
|
||||
<li>EC10 reward for less than 5 completions has been nerfed (reward at 5 completions is the same).</li>
|
||||
<li>Lowered the Dilation unlock requirement from 13000 to 12900 total TT.</li>
|
||||
<li>TP gain amount in Dilation is now calculated based on the highest AM reached.</li>
|
||||
<li>Purchasing the study to unlock Dilation now requires a 23rd row study purchase.</li>
|
||||
|
@ -194,6 +195,7 @@ Removed features:
|
|||
Bugfixes:
|
||||
<ul>
|
||||
<li>ID and replicanti autobuyer buttons are now hidden in EC8.</li>
|
||||
<li>Fixed next Sacrifice multiplier not properly displaying NC8's effect.</li>
|
||||
<li>Fixed a bug where IC5's cost increment was applied 2 times.</li>
|
||||
<li>Fixed a bug where inverted themes were broken.</li>
|
||||
<li>Fixed a bug where resetting the game unlocks a secret achievement.</li>
|
||||
|
|
|
@ -229,6 +229,9 @@ GameDatabase.credits = {
|
|||
}, {
|
||||
name: "Anthios",
|
||||
roles: 13
|
||||
}, {
|
||||
name: "Aubrey",
|
||||
roles: 13
|
||||
}, {
|
||||
name: "Auti",
|
||||
name2: "Lucia Tolle",
|
||||
|
@ -321,9 +324,6 @@ GameDatabase.credits = {
|
|||
}, {
|
||||
name: "Pavlxiiv",
|
||||
roles: 13
|
||||
}, {
|
||||
name: "Porygon-Z",
|
||||
roles: 13
|
||||
}, {
|
||||
name: "PotatoTIAB",
|
||||
roles: 13
|
||||
|
|
|
@ -27,17 +27,19 @@ GameDatabase.eternity.milestones = {
|
|||
eternities: 6,
|
||||
reward: () => {
|
||||
const EPmin = getOfflineEPGain(TimeSpan.fromMinutes(1).totalMilliseconds);
|
||||
const em200 = getEternitiedMilestoneReward(TimeSpan.fromHours(1).totalMilliseconds, true).gt(0);
|
||||
const em1000 = getInfinitiedMilestoneReward(TimeSpan.fromHours(1).totalMilliseconds, true).gt(0);
|
||||
const em200 = getEternitiedMilestoneReward(TimeSpan.fromHours(1).totalMilliseconds,
|
||||
EternityMilestone.autoEternities.isReached).gt(0);
|
||||
const em1000 = getInfinitiedMilestoneReward(TimeSpan.fromHours(1).totalMilliseconds,
|
||||
EternityMilestone.autoInfinities.isReached).gt(0);
|
||||
if (!player.options.offlineProgress) return `This milestone would give offline EP generation, but offline progress
|
||||
is currently disabled.`;
|
||||
is currently disabled`;
|
||||
const effectText = (em200 || em1000) ? "Inactive" : `Currently ${format(EPmin, 2, 2)} EP/min`;
|
||||
return `While offline, gain ${formatPercents(0.25)} of your best Eternity Points per minute from previous
|
||||
Eternities. (${effectText})`;
|
||||
Eternities (${effectText})`;
|
||||
},
|
||||
activeCondition: () => (player.options.offlineProgress
|
||||
? `Active as long as neither of the other offline milestones
|
||||
(${formatInt(200)} or ${formatInt(1000)}) are also active.`
|
||||
(${formatInt(200)} or ${formatInt(1000)}) are also active`
|
||||
: ""),
|
||||
},
|
||||
autoIC: {
|
||||
|
@ -130,34 +132,37 @@ GameDatabase.eternity.milestones = {
|
|||
},
|
||||
autobuyerEternity: {
|
||||
eternities: 100,
|
||||
reward: "Unlock autobuyer for Eternities."
|
||||
reward: "Unlock autobuyer for Eternities"
|
||||
},
|
||||
autoEternities: {
|
||||
eternities: 200,
|
||||
reward: () => {
|
||||
if (!player.options.offlineProgress) return `This milestone would generate eternities offline, but offline
|
||||
progress is currently disabled.`;
|
||||
const eternities = getEternitiedMilestoneReward(TimeSpan.fromHours(1).totalMilliseconds, true);
|
||||
progress is currently disabled`;
|
||||
const eternities = getEternitiedMilestoneReward(TimeSpan.fromHours(1).totalMilliseconds,
|
||||
player.eternities.gte(200));
|
||||
// As far as I can tell, using templates here as Codefactor wants would lead to nested templates,
|
||||
// which seems messy to say the least.
|
||||
const realTime = PlayerProgress.seenAlteredSpeed() ? " real-time" : "";
|
||||
// eslint-disable-next-line prefer-template
|
||||
return `While offline, gain Eternities at ${formatPercents(0.5)} the rate of your fastest Eternity. ` +
|
||||
return `While offline, gain Eternities at ${formatPercents(0.5)} the rate of your fastest${realTime} Eternity ` +
|
||||
(eternities.gt(0) ? `(Currently ${format(eternities, 2, 2)}/hour)` : "(Inactive)");
|
||||
},
|
||||
activeCondition: () => (player.options.offlineProgress
|
||||
? `Must be outside of all Challenges and Dilation,
|
||||
and the Eternity Autobuyer must be turned on and set to zero EP.`
|
||||
? `Must be outside of all Challenges and Dilation, and the Eternity Autobuyer must be set to Eternity at zero EP.
|
||||
This milestone's effect is capped at ${formatInt(33)}ms.`
|
||||
: ""),
|
||||
},
|
||||
autoInfinities: {
|
||||
eternities: 1000,
|
||||
reward: () => {
|
||||
if (!player.options.offlineProgress) return `This milestone would generate infinities offline, but offline
|
||||
progress is currently disabled.`;
|
||||
const infinities = getInfinitiedMilestoneReward(TimeSpan.fromHours(1).totalMilliseconds, true);
|
||||
progress is currently disabled`;
|
||||
const infinities = getInfinitiedMilestoneReward(TimeSpan.fromHours(1).totalMilliseconds,
|
||||
player.eternities.gte(1000));
|
||||
// eslint-disable-next-line prefer-template
|
||||
return `While offline, gain Infinities equal to ${formatPercents(0.5)}
|
||||
your best Infinities/hour this Eternity. ` +
|
||||
your best Infinities/hour this Eternity ` +
|
||||
(infinities.gt(0) ? `(Currently ${format(infinities, 2, 2)}/hour)` : "(Inactive)");
|
||||
},
|
||||
activeCondition: () => (player.options.offlineProgress
|
||||
|
|
|
@ -32,7 +32,9 @@ GameDatabase.eternity.upgrades = {
|
|||
id: 3,
|
||||
cost: 5e4,
|
||||
description: "Infinity Dimensions multiplier based on sum of Infinity Challenge times",
|
||||
effect: () => DC.D2.pow(30 / Time.infinityChallengeSum.totalSeconds),
|
||||
// The cap limits this at a lower value, but we also need an explicit cap here because very old versions have
|
||||
// allowed EC12 to make all the challenge records sum to zero (causing a division by zero here)
|
||||
effect: () => DC.D2.pow(30 / Math.clampMin(Time.infinityChallengeSum.totalSeconds, 0.1)),
|
||||
cap: DC.D2P30D0_61,
|
||||
formatEffect: value => formatX(value, 2, 1)
|
||||
},
|
||||
|
@ -57,7 +59,7 @@ GameDatabase.eternity.upgrades = {
|
|||
? "Time Dimensions are multiplied by days played in this Armageddon"
|
||||
: "Time Dimensions are multiplied by days played"
|
||||
),
|
||||
effect: () => (Pelle.isDoomed ? 1 + Time.thisReality.totalDays : Time.totalTimePlayed.totalDays),
|
||||
effect: () => (Pelle.isDoomed ? 1 + Time.thisReality.totalDays : Math.max(Time.totalTimePlayed.totalDays, 1)),
|
||||
formatEffect: value => formatX(value, 2, 1)
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ GameDatabase.eternity.timeStudies.ec = [
|
|||
secondary: {
|
||||
resource: "Eternities",
|
||||
current: () => Currency.eternities.value,
|
||||
required: completions => new Decimal(20000 + completions * 20000),
|
||||
required: completions => new Decimal(20000 + Math.min(completions, Enslaved.isRunning ? 999 : 4) * 20000),
|
||||
formatValue: formatInt
|
||||
}
|
||||
},
|
||||
|
@ -22,7 +22,7 @@ GameDatabase.eternity.timeStudies.ec = [
|
|||
secondary: {
|
||||
resource: "Tickspeed upgrades from Time Dimensions",
|
||||
current: () => player.totalTickGained,
|
||||
required: completions => 1300 + completions * 150,
|
||||
required: completions => 1300 + Math.min(completions, 4) * 150,
|
||||
formatValue: formatInt
|
||||
}
|
||||
},
|
||||
|
@ -34,7 +34,7 @@ GameDatabase.eternity.timeStudies.ec = [
|
|||
secondary: {
|
||||
resource: "8th Antimatter Dimensions",
|
||||
current: () => AntimatterDimension(8).totalAmount,
|
||||
required: completions => new Decimal(17300 + completions * 1250),
|
||||
required: completions => new Decimal(17300 + Math.min(completions, 4) * 1250),
|
||||
formatValue: value => formatInt(Math.floor(value.toNumber()))
|
||||
}
|
||||
},
|
||||
|
@ -46,7 +46,7 @@ GameDatabase.eternity.timeStudies.ec = [
|
|||
secondary: {
|
||||
resource: "Infinities",
|
||||
current: () => Currency.infinitiesTotal.value,
|
||||
required: completions => new Decimal(1e8 + completions * 5e7),
|
||||
required: completions => new Decimal(1e8 + Math.min(completions, 4) * 2.5e7),
|
||||
formatValue: value => formatInt(Math.floor(value.toNumber()))
|
||||
}
|
||||
},
|
||||
|
@ -58,7 +58,7 @@ GameDatabase.eternity.timeStudies.ec = [
|
|||
secondary: {
|
||||
resource: "Antimatter Galaxies",
|
||||
current: () => player.galaxies,
|
||||
required: completions => 160 + completions * 14,
|
||||
required: completions => 160 + Math.min(completions, 4) * 14,
|
||||
formatValue: formatInt
|
||||
}
|
||||
},
|
||||
|
@ -70,7 +70,7 @@ GameDatabase.eternity.timeStudies.ec = [
|
|||
secondary: {
|
||||
resource: "Replicanti Galaxies",
|
||||
current: () => player.replicanti.galaxies,
|
||||
required: completions => 40 + completions * 5,
|
||||
required: completions => 40 + Math.min(completions, 4) * 5,
|
||||
formatValue: formatInt
|
||||
}
|
||||
},
|
||||
|
@ -82,7 +82,7 @@ GameDatabase.eternity.timeStudies.ec = [
|
|||
secondary: {
|
||||
resource: "antimatter",
|
||||
current: () => Currency.antimatter.value,
|
||||
required: completions => DC.E300000.pow(completions).times(DC.E500000),
|
||||
required: completions => DC.E300000.pow(Math.min(completions, 4)).times(DC.E500000),
|
||||
formatValue: value => format(value)
|
||||
}
|
||||
},
|
||||
|
@ -94,7 +94,7 @@ GameDatabase.eternity.timeStudies.ec = [
|
|||
secondary: {
|
||||
resource: "Infinity Points",
|
||||
current: () => Currency.infinityPoints.value,
|
||||
required: completions => DC.E1000.pow(completions).times(DC.E4000),
|
||||
required: completions => DC.E1000.pow(Math.min(completions, 4)).times(DC.E4000),
|
||||
formatValue: value => format(value)
|
||||
}
|
||||
},
|
||||
|
@ -106,7 +106,7 @@ GameDatabase.eternity.timeStudies.ec = [
|
|||
secondary: {
|
||||
resource: "Infinity Power",
|
||||
current: () => Currency.infinityPower.value,
|
||||
required: completions => DC.E2000.pow(completions).times(DC.E17500),
|
||||
required: completions => DC.E2000.pow(Math.min(completions, 4)).times(DC.E17500),
|
||||
formatValue: value => format(value)
|
||||
}
|
||||
},
|
||||
|
@ -118,7 +118,7 @@ GameDatabase.eternity.timeStudies.ec = [
|
|||
secondary: {
|
||||
resource: "Eternity Points",
|
||||
current: () => Currency.eternityPoints.value,
|
||||
required: completions => DC.E20.pow(completions).times(DC.E100),
|
||||
required: completions => DC.E20.pow(Math.min(completions, 4)).times(DC.E100),
|
||||
formatValue: value => format(value)
|
||||
}
|
||||
},
|
||||
|
|
|
@ -55,8 +55,16 @@ GameDatabase.eternity.timeStudies.normal = [
|
|||
requirement: [11],
|
||||
reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE,
|
||||
description: () => `Improve Replicanti multiplier formula to
|
||||
(log2(x)${formatPow(2)})+x${formatPow(0.032, 3, 3)}`,
|
||||
effect: () => Replicanti.amount.pow(0.032)
|
||||
(log2(x)${formatPow(2)})+x${formatPow(0.032, 3, 3)}`,
|
||||
effect: () => Replicanti.amount.pow(0.032),
|
||||
// This is a special case because the study itself is *added* to the existing formula, but it makes more sense
|
||||
// to display a multiplicative increase just like every other study. We need to do the calculation in here in order
|
||||
// to properly show only the effect of this study and nothing else
|
||||
formatEffect: value => {
|
||||
const oldVal = Decimal.pow(Decimal.log2(Replicanti.amount.clampMin(1)), 2);
|
||||
const newVal = oldVal.plus(value);
|
||||
return formatX(newVal.div(oldVal).clampMin(1), 2, 2);
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
|
@ -81,7 +89,7 @@ GameDatabase.eternity.timeStudies.normal = [
|
|||
reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE,
|
||||
description: `You gain more Infinities based on Dimension Boosts`,
|
||||
effect: () => Math.max(DimBoost.totalBoosts, 1),
|
||||
formatEffect: value => formatX(value)
|
||||
formatEffect: value => formatX(value, 2)
|
||||
},
|
||||
{
|
||||
id: 33,
|
||||
|
@ -254,8 +262,8 @@ GameDatabase.eternity.timeStudies.normal = [
|
|||
requirement: [101, 102, 103],
|
||||
reqType: TS_REQUIREMENT_TYPE.AT_LEAST_ONE,
|
||||
description: () => (Achievement(103).canBeApplied
|
||||
? `Make the Infinity Point formula better log(x/${formatFloat(307.8, 1)}) ➜ log(x/${formatInt(285)})`
|
||||
: `Make the Infinity Point formula better log(x/${formatInt(308)}) ➜ log(x/${formatInt(285)})`),
|
||||
? `Make the Infinity Point formula better log(x)/${formatFloat(307.8, 1)} ➜ log(x)/${formatInt(285)}`
|
||||
: `Make the Infinity Point formula better log(x)/${formatInt(308)} ➜ log(x)/${formatInt(285)}`),
|
||||
effect: 285
|
||||
},
|
||||
{
|
||||
|
|
|
@ -188,11 +188,43 @@ ${PlayerProgress.realityUnlocked()
|
|||
isUnlocked: () => true,
|
||||
tags: ["effect", "stack", "combine", "add", "reduce", "multiply", "divide", "power", "dilation", "glyph"],
|
||||
tab: "options/gameplay"
|
||||
}, {
|
||||
name: "Common Abbreviations",
|
||||
info: () => `
|
||||
Many resources within the game may appear in an abbreviated format as text in order to save space. This How to
|
||||
Play entry will update itself with additional entries for new resources as you encounter them for the first time
|
||||
<br>
|
||||
- <b>AM</b>: Antimatter<br>
|
||||
- <b>AD</b>: Antimatter Dimension<br>
|
||||
- <b>AG</b>: Antimatter Galaxy<br>
|
||||
${PlayerProgress.infinityUnlocked() ? "- <b>IP</b>: Infinity Point<br>" : ""}
|
||||
${PlayerProgress.infinityUnlocked() ? "- <b>NC</b>: Normal Challenge<br>" : ""}
|
||||
${PlayerProgress.infinityUnlocked() ? "- <b>IC</b>: Infinity Challenge<br>" : ""}
|
||||
${InfinityDimension(1).isUnlocked || PlayerProgress.eternityUnlocked() ? "- <b>ID</b>: Infinity Dimension<br>" : ""}
|
||||
${PlayerProgress.replicantiUnlocked() ? "- <b>RG</b>: Replicanti Galaxy<br>" : ""}
|
||||
${PlayerProgress.eternityUnlocked() ? "- <b>EP</b>: Eternity Point<br>" : ""}
|
||||
${PlayerProgress.eternityUnlocked() ? "- <b>TT</b>: Time Theorem<br>" : ""}
|
||||
${PlayerProgress.eternityUnlocked() ? "- <b>TD</b>: Time Dimension<br>" : ""}
|
||||
${PlayerProgress.eternityUnlocked() ? "- <b>EC</b>: Eternity Challenge<br>" : ""}
|
||||
${PlayerProgress.dilationUnlocked() ? "- <b>TP</b>: Tachyon Particle<br>" : ""}
|
||||
${PlayerProgress.dilationUnlocked() ? "- <b>DT</b>: Dilated Time<br>" : ""}
|
||||
${PlayerProgress.dilationUnlocked() ? "- <b>TG</b>: Tachyon Galaxy<br>" : ""}
|
||||
${PlayerProgress.realityUnlocked() ? "- <b>RM</b>: Reality Machine<br>" : ""}
|
||||
${PlayerProgress.realityUnlocked() ? "- <b>AP</b>: Automator Point<br>" : ""}
|
||||
${PlayerProgress.realityUnlocked() ? "- <b>BH</b>: Black Hole<br>" : ""}
|
||||
${MachineHandler.isIMUnlocked ? "- <b>iM</b>: Imaginary Machine<br>" : ""}
|
||||
${Laitela.isUnlocked ? "- <b>DM</b>: Dark Matter<br>" : ""}
|
||||
${Laitela.isUnlocked ? "- <b>DE</b>: Dark Energy<br>" : ""}
|
||||
`,
|
||||
isUnlocked: () => true,
|
||||
tags: ["abbreviation", "shorten", "am", "ad", "ag", "ip", "nc", "ic", "id", "rg", "ep", "tt", "td", "ec", "tp",
|
||||
"dt", "tg", "rm", "ap", "bh", "im", "dm", "de"],
|
||||
tab: ""
|
||||
}, {
|
||||
name: "Antimatter Dimensions",
|
||||
info: () => `
|
||||
Antimatter is a resource that is throughout the entire game for purchasing various things as you progress. You start
|
||||
with ${formatInt(10)} antimatter when you first open the game. And you can
|
||||
Antimatter is a resource that is used throughout the entire game for purchasing various things as you progress. You
|
||||
start with ${formatInt(10)} antimatter when you first open the game, and you can
|
||||
spend it to buy the 1st Antimatter Dimension to start the game.
|
||||
<br>
|
||||
<br>
|
||||
|
@ -305,7 +337,7 @@ available, but will increase the effect of your Tickspeed Upgrades by +${format(
|
|||
Galaxies. As you get more Galaxies, the multiplier will continue becoming stronger and stronger.
|
||||
<br>
|
||||
<br>
|
||||
Though it will have very little impact for the first few purchases,
|
||||
Though it will have very little impact for the first few Tickspeed purchases,
|
||||
the increase is multiplicative and won't take long to be visible.
|
||||
<br>
|
||||
<br>
|
||||
|
@ -328,7 +360,7 @@ increases by another ${formatPercents(0.002, 1)} per Galaxy, on top of Distant s
|
|||
}, {
|
||||
name: "Dimensional Sacrifice",
|
||||
info: () => `
|
||||
<b>You unlock Dimensional Sacrifice after your first Dimension Boost.</b>
|
||||
<b>You unlock Dimensional Sacrifice after your fifth Dimension Boost.</b>
|
||||
<br>
|
||||
<br>
|
||||
Sacrificing will immediately reset the owned quantity of all non-Eighth Dimensions to zero, without reducing the
|
||||
|
@ -476,7 +508,7 @@ It does not change individual autobuyer settings. Think of it like a master swit
|
|||
Additionally, holding <b>Alt</b> when pressing a hotkey associated with an upgrade, dimension, or prestige will
|
||||
toggle the associated autobuyer.
|
||||
`,
|
||||
isUnlocked: () => PlayerProgress.infinityUnlocked(),
|
||||
isUnlocked: () => true,
|
||||
tags: ["infinity", "automation", "challenges", "rewards", "interval", "earlygame"],
|
||||
tab: "automation/autobuyers"
|
||||
}, {
|
||||
|
@ -732,7 +764,7 @@ to buy your preferred path and continue on instead of stopping completely at the
|
|||
for the Dimension split in this dialog if you have purchased the relevant Time Study.
|
||||
<br>
|
||||
<br>
|
||||
<b>Respecs:</b> A respec allows you to reset the upgrades you have in the tree to retreive all of the Time Theorems
|
||||
<b>Respecs:</b> A respec allows you to reset the upgrades you have in the tree to retrieve all of the Time Theorems
|
||||
spent on them. It can be done for free, but only triggers on finishing an Eternity; you can't respec Time Studies in
|
||||
the middle of an Eternity.
|
||||
<br>
|
||||
|
@ -769,9 +801,10 @@ goal to the next completion also increases. Additionally, the secondary requirem
|
|||
also increase. The Time Theorem cost does not increase.
|
||||
<br>
|
||||
<br>
|
||||
After you have unlocked an Eternity Challenge, you can unlock it without meeting its secondary requirements until you
|
||||
unlock another Eternity Challenge or beat that Eternity Challenge, allowing you to unlock an Eternity Challenge
|
||||
with one set of studies, and then respec into a different set of studies to beat the challenge.
|
||||
Completing an Eternity Challenge's secondary requirements will remove them from the study requirement until you complete
|
||||
that particular Eternity Challenge, meaning you only need to complete the secondary requirement <i>once</i>.
|
||||
As a result, you can unlock an Eternity Challenge with one set of studies, and then respec into a different set of
|
||||
studies to beat the challenge.
|
||||
`,
|
||||
isUnlocked: () => PlayerProgress.eternityUnlocked(),
|
||||
tags: ["ec", "study", "time", "rewards", "completions", "midgame"],
|
||||
|
@ -1109,6 +1142,10 @@ Unlocking or defeating a Celestial has different conditions depending on the Cel
|
|||
<br>
|
||||
All Celestials have their own Celestial Reality, but how the Reality is relevant to each Celestial and the rest of
|
||||
the game will depend on the Celestial.
|
||||
<br>
|
||||
<br>
|
||||
Celestials are timeless entities. Unless otherwise stated, any new mechanics introduced by Celestials are not affected
|
||||
by game speed multipliers and instead refer specifically to real time instead of game time.
|
||||
`,
|
||||
isUnlocked: () => Teresa.isUnlocked,
|
||||
tags: ["reality", "challenges", "endgame", "lategame"],
|
||||
|
@ -1233,6 +1270,12 @@ being selected; this can be useful for effect testing and other more limited sit
|
|||
will let you forbid entire types like Specified Effect Mode as well
|
||||
<br>
|
||||
<br>
|
||||
The Glyph Filter mode is a global setting which applies to all Glyph types at once; for example, you cannot filter
|
||||
power Glyphs with "Rarity Threshold" and time Glyphs with "Specified Effect". Selecting one mode will require
|
||||
you to configure every Glyph type within its settings for proper filtering. Each filter mode has its own settings
|
||||
which will be kept if you switch to another mode.
|
||||
<br>
|
||||
<br>
|
||||
Glyph Presets are purchasable for ${format(GameDatabase.celestials.effarig.unlocks.setSaves.cost)} Relic
|
||||
Shards. This unlocks ${formatInt(5)} slots that allow you to save your currently equipped Glyphs into sets.
|
||||
You can't overwrite a set, you must delete it first. When you load a set, each Glyph in it is found and equipped.
|
||||
|
@ -1463,7 +1506,8 @@ with a higher refinement value.
|
|||
Alchemy Resources can be combined together in certain combinations in order to create new compound resources, which
|
||||
are unlocked at certain Effarig levels. Resources are combined once per Reality, unaffected by real time
|
||||
amplification. Reactions have a higher yield and thus happen faster when your reagent amounts are higher. The cap for
|
||||
compound resources is equal to the lowest cap amongst all of its reagents.
|
||||
compound resources is equal to the lowest cap amongst all of its reagents. In order for a reaction to occur, the
|
||||
current amount of all reagents must be greater than the current amount of the produced resource.
|
||||
<br>
|
||||
<br>
|
||||
To activate or deactivate a reaction, click the circle corresponding to the reaction's product. When the reaction can
|
||||
|
|
|
@ -85,7 +85,10 @@ GameDatabase.infinity.breakUpgrades = {
|
|||
Ra.unlocks.continuousTTBoost.effects.infinity
|
||||
);
|
||||
infinities = infinities.times(getAdjustedGlyphEffect("infinityinfmult"));
|
||||
return `${quantify("Infinity", infinities)} every ${Time.bestInfinity.times(5).toStringShort()}`;
|
||||
const timeStr = Time.bestInfinity.totalMilliseconds <= 50
|
||||
? `${TimeSpan.fromMilliseconds(100).toStringShort()} (capped)`
|
||||
: `${Time.bestInfinity.times(2).toStringShort()}`;
|
||||
return `${quantify("Infinity", infinities)} every ${timeStr}`;
|
||||
}
|
||||
},
|
||||
autobuyMaxDimboosts: {
|
||||
|
@ -135,8 +138,7 @@ GameDatabase.infinity.breakUpgrades = {
|
|||
if (!BreakInfinityUpgrade.ipGen.isCapped) {
|
||||
generation += ` ➜ ${formatInt(5 * (1 + player.infinityRebuyables[2]))}%`;
|
||||
}
|
||||
const offlineString = player.options.offlineProgress ? ", works offline" : "";
|
||||
return `${generation} of your best IP/min from your last 10 Infinities${offlineString}`;
|
||||
return `${generation} of your best IP/min from your last 10 Infinities`;
|
||||
},
|
||||
isDisabled: effect => effect.eq(0),
|
||||
formatEffect: value => `${format(value, 2, 1)} IP/min`,
|
||||
|
|
|
@ -165,11 +165,8 @@ GameDatabase.infinity.upgrades = {
|
|||
formatEffect: value => {
|
||||
if (Teresa.isRunning || V.isRunning) return "Disabled in this reality";
|
||||
if (Pelle.isDoomed) return "Disabled";
|
||||
const income = format(value, 2, 0);
|
||||
const period = player.records.bestInfinity.time >= 999999999999
|
||||
? "∞"
|
||||
: Time.bestInfinity.times(10).toStringShort();
|
||||
return `${income} every ${period}`;
|
||||
if (player.records.bestInfinity.time >= 999999999999) return "Too slow to generate";
|
||||
return `${format(value, 2)} every ${Time.bestInfinity.times(10).toStringShort()}`;
|
||||
},
|
||||
charged: {
|
||||
description: () =>
|
||||
|
|
|
@ -83,7 +83,8 @@ GameDatabase.multiplierTabValues.gamespeed = {
|
|||
},
|
||||
chargingBH: {
|
||||
name: "Black Hole Charging",
|
||||
multValue: () => 1 - player.celestials.enslaved.storedFraction,
|
||||
// The 0 in multValue is irrelevant; if this upgrade isn't available, the subtab is hidden by 1x total effect
|
||||
multValue: () => (Ra.unlocks.autoPulseTime.canBeApplied ? 0.01 : 0),
|
||||
isActive: () => Enslaved.isStoringGameTime,
|
||||
icon: MultiplierTabIcons.BLACK_HOLE,
|
||||
},
|
||||
|
|
|
@ -23,10 +23,9 @@ GameDatabase.multiplierTabValues.ID = {
|
|||
.filter(id => id.isProducing)
|
||||
.map(id => id.multiplier)
|
||||
.reduce((x, y) => x.times(y), DC.D1)),
|
||||
isActive: dim => !EternityChallenge(11).isRunning &&
|
||||
(dim
|
||||
? InfinityDimension(dim).isProducing
|
||||
: (PlayerProgress.eternityUnlocked() || InfinityDimension(1).isProducing)),
|
||||
isActive: dim => (dim
|
||||
? InfinityDimension(dim).isProducing
|
||||
: (PlayerProgress.eternityUnlocked() || InfinityDimension(1).isProducing)),
|
||||
dilationEffect: () => {
|
||||
const baseEff = player.dilation.active
|
||||
? 0.75 * Effects.product(DilationUpgrade.dilationPenalty)
|
||||
|
|
|
@ -23,8 +23,9 @@ GameDatabase.multiplierTabValues.TD = {
|
|||
.filter(td => td.isProducing)
|
||||
.map(td => td.multiplier)
|
||||
.reduce((x, y) => x.times(y), DC.D1)),
|
||||
isActive: dim => !EternityChallenge(11).isRunning &&
|
||||
(dim ? TimeDimension(dim).isProducing : (PlayerProgress.realityUnlocked() || TimeDimension(1).isProducing)),
|
||||
isActive: dim => (dim
|
||||
? TimeDimension(dim).isProducing
|
||||
: (PlayerProgress.realityUnlocked() || TimeDimension(1).isProducing)),
|
||||
dilationEffect: () => {
|
||||
const baseEff = player.dilation.active
|
||||
? 0.75 * Effects.product(DilationUpgrade.dilationPenalty)
|
||||
|
|
|
@ -223,7 +223,9 @@ GameDatabase.news = [
|
|||
},
|
||||
{
|
||||
id: "a49",
|
||||
text: "Can we get 1e169 likes on this video??? Smash that like button!!"
|
||||
get text() {
|
||||
return `Can we get ${format(1e169)} likes on this video??? Smash that like button!!`;
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "a50",
|
||||
|
@ -317,7 +319,7 @@ GameDatabase.news = [
|
|||
},
|
||||
{
|
||||
id: "a70",
|
||||
text: "If you can't read this you disabled the news."
|
||||
text: "If you can't read this, you disabled the news."
|
||||
},
|
||||
{
|
||||
id: "a71",
|
||||
|
@ -424,7 +426,7 @@ GameDatabase.news = [
|
|||
link: "https://trimps.github.io/"
|
||||
},
|
||||
{
|
||||
name: "Mine Defense",
|
||||
name: "Mine Defense (the game's ui is broken on https so make sure you're on http!)",
|
||||
link: "http://scholtek.com/minedefense"
|
||||
},
|
||||
{
|
||||
|
@ -450,6 +452,10 @@ GameDatabase.news = [
|
|||
{
|
||||
name: "The First Alkahistorian stages 1, 2, and 3",
|
||||
link: "https://nagshell.github.io/elemental-inception-incremental/"
|
||||
},
|
||||
{
|
||||
name: "Melvor Idle",
|
||||
link: "https://melvoridle.com/"
|
||||
}
|
||||
];
|
||||
const game = games.randomElement();
|
||||
|
@ -507,7 +513,8 @@ GameDatabase.news = [
|
|||
id: "a103",
|
||||
text:
|
||||
`Antimatter... antimatter never changes... until you get to quantum physics of antimatter,
|
||||
but we don't have enough tachyon particles for that.`
|
||||
but we don't have enough tachyon particles for that.`,
|
||||
get unlocked() { return PlayerProgress.realityUnlocked() || PlayerProgress.dilationUnlocked(); }
|
||||
},
|
||||
{
|
||||
id: "a104",
|
||||
|
@ -597,7 +604,8 @@ GameDatabase.news = [
|
|||
Sacrifice' is moving away. You have to give up a lot of the things you had that made you happy, but there is
|
||||
new opportunity in where you move to. And that new opportunity gives you more happiness than you ever had.
|
||||
'Tickspeed' is how easy it is to make you happy, and 'Time Dimensions' make it even easier to be happy.
|
||||
Antimatter Dimensions is a metaphor for a depressed man's successful battle against his illness.`
|
||||
Antimatter Dimensions is a metaphor for a depressed man's successful battle against his illness.`,
|
||||
get unlocked() { return PlayerProgress.eternityUnlocked(); }
|
||||
},
|
||||
{
|
||||
id: "a114",
|
||||
|
@ -715,7 +723,8 @@ GameDatabase.news = [
|
|||
},
|
||||
{
|
||||
id: "a134",
|
||||
text: "Because of this game I can now use the word \"infinity\" as a verb."
|
||||
text: "Because of this game I can now use the word \"infinity\" as a verb.",
|
||||
get unlocked() { return PlayerProgress.infinityUnlocked(); }
|
||||
},
|
||||
{
|
||||
id: "a135",
|
||||
|
@ -841,7 +850,8 @@ GameDatabase.news = [
|
|||
id: "a159",
|
||||
text:
|
||||
`What does it mean when you "bank" Infinities? Is there a bank somewhere that you just deposit these
|
||||
infinities? Does having a lot of banked Infinities improve your credit score? Do you get a credit card?`
|
||||
infinities? Does having a lot of banked Infinities improve your credit score? Do you get a credit card?`,
|
||||
get unlocked() { return PlayerProgress.eternityUnlocked(); }
|
||||
},
|
||||
{
|
||||
id: "a160",
|
||||
|
@ -1277,7 +1287,8 @@ GameDatabase.news = [
|
|||
},
|
||||
{
|
||||
id: "a223",
|
||||
text: "If you find your infinity lasting longer than 5 hours please contact a medical professional."
|
||||
text: "If you find your infinity lasting longer than 5 hours please contact a medical professional.",
|
||||
get unlocked() { return PlayerProgress.infinityUnlocked(); }
|
||||
},
|
||||
{
|
||||
id: "a224",
|
||||
|
@ -1432,6 +1443,9 @@ GameDatabase.news = [
|
|||
${BLOB} are just blobbling and bouncing around, occasionally merging and dividing. Only ${BLOB} know where
|
||||
they are from or where they are going to go. Still, ${BLOB} are there, always with me.
|
||||
You love ${BLOB}, so ${BLOB} loves you too.`,
|
||||
S12:
|
||||
`it makes you feel warm and comfortable, as if you were right at home. However, it is highly recommended
|
||||
to update your theme to the newest theme for the best user experience.`,
|
||||
};
|
||||
const reason = reasons[Theme.current().name.replace(/\s/gu, "")];
|
||||
return `Ah, a fellow ${theme} theme user. I see that you have impeccable taste.
|
||||
|
@ -1507,6 +1521,7 @@ GameDatabase.news = [
|
|||
get text() {
|
||||
return `<span style='animation: a-text-stretch ${newsAnimSpd(35)}s 1 forwards'>This message is dilated.</span>`;
|
||||
},
|
||||
get unlocked() { return PlayerProgress.realityUnlocked() || PlayerProgress.dilationUnlocked(); }
|
||||
},
|
||||
{
|
||||
id: "a253",
|
||||
|
@ -1570,7 +1585,8 @@ GameDatabase.news = [
|
|||
id: "a260",
|
||||
text:
|
||||
`It seems that the Replicanti have a very divide-and-conquer method of doing things.
|
||||
Well, everything at this rate.`
|
||||
Well, everything at this rate.`,
|
||||
get unlocked() { return PlayerProgress.eternityUnlocked() || PlayerProgress.replicantiUnlocked(); }
|
||||
},
|
||||
{
|
||||
id: "a261",
|
||||
|
@ -2279,8 +2295,8 @@ GameDatabase.news = [
|
|||
}
|
||||
};
|
||||
}()),
|
||||
// Blob from the blob font
|
||||
{
|
||||
// Blob from the blob font
|
||||
id: "a354",
|
||||
text:
|
||||
`<span style='color: #FBC21B; text-shadow: 0px 1px 0px black, 1px 0px 0px black, 1px 1px 0px black,
|
||||
|
@ -2316,13 +2332,13 @@ GameDatabase.news = [
|
|||
id: "a360",
|
||||
text: `Press "Choose save" to explore the other 2 parallel universes.`
|
||||
},
|
||||
// Discord contest winner #1
|
||||
{
|
||||
// Discord contest winner #1
|
||||
id: "a361",
|
||||
text: "We're having a sale of top quality waterproof towels! Be sure to get some on your way out!"
|
||||
},
|
||||
// Discord contest winner #2
|
||||
{
|
||||
// Discord contest winner #2
|
||||
id: "a362",
|
||||
text:
|
||||
`Hevipelle Incorporated is proud to present a new brand of cereal: The Big Crunch! This nutritious breakfast
|
||||
|
@ -2330,7 +2346,8 @@ GameDatabase.news = [
|
|||
Replicanti, and Eternity-flavored Marshmallows. Now you can experience Antimatter Dimensions inside of your
|
||||
stomach! Warning: Side effects may include spontaneous combustion, nausea, vomiting, diarrhea,
|
||||
dematerialization, vaporization, heart failure, the end of the world, or death. If you are not made out of
|
||||
antimatter, consult an educated professional on Antimatter Consumption before eating 'The Big Crunch'.`
|
||||
antimatter, consult an educated professional on Antimatter Consumption before eating 'The Big Crunch'.`,
|
||||
get unlocked() { return PlayerProgress.eternityUnlocked(); }
|
||||
},
|
||||
{
|
||||
id: "a363",
|
||||
|
@ -2368,7 +2385,8 @@ GameDatabase.news = [
|
|||
},
|
||||
{
|
||||
id: "a365",
|
||||
text: "I don't like Replicanti. They're coarse and rough and irritating and they replicate everywhere."
|
||||
text: "I don't like Replicanti. They're coarse and rough and irritating and they replicate everywhere.",
|
||||
get unlocked() { return PlayerProgress.eternityUnlocked() || PlayerProgress.replicantiUnlocked(); }
|
||||
},
|
||||
{
|
||||
id: "a366",
|
||||
|
@ -2437,6 +2455,148 @@ GameDatabase.news = [
|
|||
at the WAIC (Witches Annual Infrastructure Committee) as part of stage 56. Truly, tragic stuff - 3 award
|
||||
nominations and 2 wins during that process due to EBIF (Efficient Bureaucracy In (the) Field).`
|
||||
},
|
||||
{
|
||||
id: "a370",
|
||||
text:
|
||||
"Man tries installing cookies to store computer data, accidentally cleans them due to being too delicious."
|
||||
},
|
||||
{
|
||||
id: "a371",
|
||||
text:
|
||||
`Pop quiz: there are 3 doors, you pick a door at random, and get to keep what's behind the door. The doors
|
||||
have 2 golden goats, 2 silver goats, and a gold and a silver goat. After you pick a door, the door with the
|
||||
lowest $ worth of goats will be opened and shown to you. After this, you are given the choice to swap.
|
||||
What is the probability that you will swap doors?`
|
||||
},
|
||||
{
|
||||
id: "a372",
|
||||
text:
|
||||
`If you're ever lost in a forest, look at the trees around you. It's said that moss grows north, so by the
|
||||
time you've finished looking at a tree, a roaming guitarist will run up to you and ask if you want to hear
|
||||
wonderwall`
|
||||
},
|
||||
{
|
||||
id: "a373",
|
||||
text:
|
||||
`As a symbol of friendship between the Matter and Antimatter Periodic Tables, they have done an exchange of
|
||||
elements. The element of Mony is now part of the Antimatter Periodic Table, while Antimony has been added
|
||||
to the regular Periodic Table.`
|
||||
},
|
||||
{
|
||||
id: "a374",
|
||||
text: "This newsticker was specifically designed for promotional purposes only."
|
||||
},
|
||||
{
|
||||
id: "a375",
|
||||
text:
|
||||
`As you probably know, it is traditional to give gifts made of certain materials to celebrate anniversaries
|
||||
The classic ones are silver at 25 and gold at 50. Here are some little known anniversary gifts:
|
||||
Pineapple - 37 years Hellstone - 66 years Lizardite- 82 years Nitrowhisperin- 86 years Taconite - 95 years
|
||||
Hatchettite - 100 years Electrum - 110 Yakitoda - 111 years years Fordite - 119 years Bloodstone - 120 years
|
||||
Celestite - 125 years Jet - 140 years Petroleum - 145 years Steel - 150 years Cummingtonite - 198 years
|
||||
Concrete - 200 years Laserblue- 210 years Painite - 250 years Parisite - 255 years Parasite - 260 years
|
||||
Carbon Nanotubes - 300 years Mercury - 310 years Martian Soil - 340 years Neptunium - 370 years
|
||||
Uranium - 380 years Plutonium - 390 years Xium - 400 years Blaze rods - 420 years Asbestos - 430 years
|
||||
Gabite - 444 years Crimtane - 666 years Lagga - 777 years`
|
||||
},
|
||||
{
|
||||
id: "a376",
|
||||
text:
|
||||
`Big tech companies have collaborated to create a new neural network that's trained in the generation of rap
|
||||
lyrics, called RAP-3. First lyrical generations include "Call me prometheus 'cuz I bring the fire" and
|
||||
"Call me Sonic the way I'm gettin' these rings". Critics say it still has a way to go before it replaces
|
||||
traditional music.`
|
||||
},
|
||||
{
|
||||
id: "a377",
|
||||
text:
|
||||
`With the new android OS, android 20, being predicted in the near future, the new system for internal codenames
|
||||
has been revealed. The first codename, as it currently stands, is “antimatter”. This conveniently works well
|
||||
with the predicted generation of phones that will use Android 20 - these phones will be the most explosive
|
||||
ever due to their annihilation-based power source. Sources tell us that a billion dollar research unit is
|
||||
working on a name for android 21, by tradition to start with B, “that doesn't sound too bad when you think
|
||||
about it”. `
|
||||
},
|
||||
{
|
||||
id: "a378",
|
||||
text: "If every antimatter were an apple, you would have enough to keep all the doctors away for 3000 years"
|
||||
},
|
||||
{
|
||||
id: "a379",
|
||||
get text() {
|
||||
return `THE ${format(Number.MAX_VALUE, 2)} PIECE! THE ${format(Number.MAX_VALUE, 2)} PIECE IS REAL!`;
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "a380",
|
||||
text:
|
||||
`The FitnessGram Pacer Test is a multistage aerobic capacity test that progressively gets more difficult
|
||||
as it continues. The 20 meter pacer test will begin in 30 seconds. Line up at the start. The running speed
|
||||
starts slowly, but gets faster each minute after you hear this signal. [beep] A single lap should be
|
||||
completed each time you hear this sound. [ding] Remember to run in a straight line, and run as long as
|
||||
possible. The second time you fail to complete a lap before the sound, your test is over. The test will
|
||||
begin on the word start. On your mark, get ready, start.`
|
||||
},
|
||||
{
|
||||
id: "a381",
|
||||
text: "Why do they call it second dimension when you of in the first dimension of out second eat the dimension?"
|
||||
},
|
||||
{
|
||||
id: "a382",
|
||||
text:
|
||||
"Any AD player born after 1993 can't joke... All they know is 5 hours, paperclips, 1.79e308 & Ninth Dimension."
|
||||
},
|
||||
{
|
||||
id: "a383",
|
||||
text:
|
||||
"The only thing better than an anti-joke is two. Like the number. Not two anti-jokes. I just like the number two."
|
||||
},
|
||||
{
|
||||
id: "a384",
|
||||
text: "Click here to make nothing happen."
|
||||
},
|
||||
{
|
||||
id: "a385",
|
||||
text:
|
||||
`I wonder... Why did Apple skip iPhone 9 and Microsoft skip Windows 9...
|
||||
Was it because they were bribed by a game developer?`
|
||||
},
|
||||
{
|
||||
id: "a386",
|
||||
text: "9 out of 10 doctors recommended against trying to touch antimatter. We haven't heard back from the 10th one."
|
||||
},
|
||||
{
|
||||
id: "a387",
|
||||
text:
|
||||
`In spring, Man built a pillar. In summer, another. Throughout autumn they held. But in winter, one experienced
|
||||
an unexpected (See definition in: Abstract Multidimensional Retrocausal Physics) ZW-Class "Ascension" event,
|
||||
and is hypothesised to have fallen into a dimensional loophole, where it, by definition, has to take up more
|
||||
dimensions than itself. Current efforts at retrieving the pillar and returning it to baseline reality have been
|
||||
unsuccessful (See test log 2453-3e9a-50d1-84fc for more details)`
|
||||
},
|
||||
{
|
||||
id: "a388",
|
||||
text:
|
||||
`In light of recent events, we'd like to issue an official statement. Antimatter Dimensions™ is in no way
|
||||
affiliated with Jimmy's Causality Violating Brainworms™. We do not endorse, no were we involved in their creation
|
||||
of the product which was involved in several catastrophic dimension-destabilising and reality-toppling incidents.
|
||||
We almost certainly did not sign a contract at 5:30:26 UTC on 08/12/1994 after discussing how we could benefit
|
||||
from destabilising and warping dimensions. There was no industrial zone constructed in the 5th Orion Arm of the
|
||||
వ' galaxy, and even if they were we did not install localised anomalies following the Scranton Reality Anti-anchor
|
||||
mechanism. Additionally, no time loop is occurring at Acroamatic Abatement Facility AAF-D in site 43. We apologise
|
||||
if things seemed this way, and we will be more thorough in cracking down misinformation in the future.`
|
||||
},
|
||||
{
|
||||
id: "a389",
|
||||
text: "If only we could condense the antimatter in the universe into cookies..."
|
||||
},
|
||||
{
|
||||
id: "a390",
|
||||
text:
|
||||
`Can you believe it guys? Update, just 5 hours away. Update is in a 5 hours. Wahoo. I'm so happy about this
|
||||
information. Update just 5 hours away. Oh wow. Can you believe it? Update just in a 5 hours. It got here so
|
||||
fast. Update, just 5 hours.`
|
||||
},
|
||||
{
|
||||
id: "l1",
|
||||
text: "You just made your 1,000,000,000,000,000th antimatter. This one tastes like chicken.",
|
||||
|
@ -3067,6 +3227,62 @@ GameDatabase.news = [
|
|||
// 3 years of time to write
|
||||
get unlocked() { return Currency.antimatter.value.gte("1e777600"); }
|
||||
},
|
||||
{
|
||||
id: "l83",
|
||||
text:
|
||||
`AD Patch Notes: Cleaned up the celestial problem Made Antimatter care about annihilation more Added mouths
|
||||
Removed mouths Stopped unwanted interlopers from corporate takeovers of shops Fixed problem with newstickers
|
||||
hanging in the air Dead replicanti remain in their galaxies Redefined interlopers to not include [REDACTED]
|
||||
Tachyon Particles get stuck in the top left corner of the screen, obliterate time Added Coriolis effect to
|
||||
Galaxy Spin Direction`,
|
||||
get unlocked() { return Teresa.isUnlocked; }
|
||||
},
|
||||
{
|
||||
id: "l84",
|
||||
get text() {
|
||||
return `For the record, you currently have ${player.news.specialTickerData.paperclips}
|
||||
Useless Paperclips. You may want to spend them on something.`;
|
||||
},
|
||||
get unlocked() { return player.news.specialTickerData.paperclips > 0; }
|
||||
},
|
||||
{
|
||||
id: "l85",
|
||||
text:
|
||||
`On opposite day, the new update is just -5 hours away. You begin increasing your Matter. Once you acquire a
|
||||
huge abundance of Matter, you must become Infinitesimal. After increasing your wealth in Infinitesimal Points,
|
||||
you can eventually Jiffy, the shortest unit of time. After enough time, your Jiffies will accumulate, and you
|
||||
will Contract Time. Contracting Time will grant you enough of a boost to eventually Fantasy, the final layer
|
||||
of maintenance. However, you find out that it was all a dream. Your Antimatter is safe and well, and the new
|
||||
update is still just 5 hours away.`,
|
||||
get unlocked() { return PlayerProgress.realityUnlocked(); }
|
||||
},
|
||||
{
|
||||
id: "l86",
|
||||
text:
|
||||
`Hello, player. I'd like to play a game. In front of you is a pile of replicanti. They are currently frozen in
|
||||
time, and cannot replicate. To your right is a computer playing Antimatter Dimensions on an empty save. You
|
||||
must reach infinity. However, once you buy a 1st dimension, the replicanti will start replicating. As you know,
|
||||
they replicate fast, and if they fill up the room you will suffocate. If you reach infinity before that, they
|
||||
will be frozen again. The clock is ticking. Start now.`,
|
||||
get unlocked() { return PlayerProgress.replicantiUnlocked(); }
|
||||
},
|
||||
{
|
||||
id: "l87",
|
||||
text:
|
||||
`"To see a World in a Grain of Sand. And a Heaven in a Wild Flower. Hold Infinity in the palm of your hand.
|
||||
And Eternity in an hour. And Reality in about 5 hours™️" ~Anti-William Blake `,
|
||||
get unlocked() { return PlayerProgress.realityUnlocked(); }
|
||||
},
|
||||
{
|
||||
id: "l88",
|
||||
text:
|
||||
`Our deepest apologies for the new glyph mechanic. The intent is to provide players with a sense of pride and
|
||||
accomplishment for unlocking rare glyphs. We selected initial values based upon data from the final wave of
|
||||
testing and other adjustments made to milestone rewards before launch. Among other things, we're looking at
|
||||
average per-player credit earn rates on a daily basis, and we'll be making constant adjustments to ensure that
|
||||
players have challenges that are compelling, rewarding, and of course attainable via gameplay.`,
|
||||
get unlocked() { return PlayerProgress.realityUnlocked(); }
|
||||
},
|
||||
{
|
||||
id: "r1",
|
||||
text: "This news message is 100x rarer than all the others.",
|
||||
|
|
|
@ -59,7 +59,7 @@ GameDatabase.progressStages = [
|
|||
name: "Eternity",
|
||||
hasReached: save => new Decimal(save.eternities).gt(0),
|
||||
suggestedResource: "Eternity Points and Eternity count",
|
||||
subProgressValue: save => Math.sqrt(new Decimal(save.eternityPoints).pLog10() / 18),
|
||||
subProgressValue: save => new Decimal(save.eternities).clampMax(1e5).toNumber() / 1e5,
|
||||
},
|
||||
{
|
||||
id: PROGRESS_STAGE.ETERNITY_CHALLENGES,
|
||||
|
|
|
@ -139,7 +139,7 @@ GameDatabase.reality.glyphCosmeticSets = {
|
|||
celestial: {
|
||||
id: "celestial",
|
||||
name: "Celestial Icons",
|
||||
symbol: ["⌬", "ᛝ", "♅"],
|
||||
symbol: ["\uF0C1", "⌬", "ᛝ", "♅"],
|
||||
color: ["B#00BCD4"],
|
||||
},
|
||||
alchemy: {
|
||||
|
|
|
@ -222,20 +222,20 @@ GameDatabase.reality.glyphEffects = {
|
|||
isGenerated: true,
|
||||
glyphTypes: ["replication"],
|
||||
singleDesc: () => (GlyphAlteration.isAdded("replication")
|
||||
? "Multiply Dilated Time \n[and Replicanti speed] by \nlog₁₀(replicanti)×{value}"
|
||||
: "Multiply Dilated Time gain by \nlog₁₀(replicanti)×{value}"),
|
||||
? `Multiply Dilated Time \n[and Replicanti speed] by \n+{value} per ${format(DC.E10000)} replicanti`
|
||||
: `Multiply Dilated Time gain by \n+{value} per ${format(DC.E10000)} replicanti`),
|
||||
totalDesc: () => (GlyphAlteration.isAdded("replication")
|
||||
? "Dilated Time gain and Replication speed ×(log₁₀(replicanti)×{value})"
|
||||
: "Dilated Time gain ×(log₁₀(replicanti)×{value})"),
|
||||
? `Multiply Dilated Time and Replication speed by +{value} per ${format(DC.E10000)} replicanti`
|
||||
: `Multiply Dilated Time gain by +{value} per ${format(DC.E10000)} replicanti`),
|
||||
genericDesc: () => (GlyphAlteration.isAdded("replication")
|
||||
? "Dilated Time+Replicanti mult (log₁₀(replicanti))"
|
||||
: "Dilated Time gain multiplier (log₁₀(replicanti))"),
|
||||
? "Dilated Time+Replicanti mult from replicanti"
|
||||
: "Dilated Time gain multiplier from replicanti"),
|
||||
shortDesc: () => (GlyphAlteration.isAdded("replication")
|
||||
? "DT and repl. ×log₁₀(repl.)×{value}"
|
||||
: "DT ×log₁₀(repl.)×{value}"),
|
||||
? `×DT and repl. by +{value} per ${format(DC.E10000)} replicanti`
|
||||
: `×DT by +{value} per ${format(DC.E10000)} replicanti`),
|
||||
effect: (level, strength) => 0.0003 * Math.pow(level, 0.3) * Math.pow(strength, 0.65),
|
||||
formatEffect: x => format(x, 5, 5),
|
||||
formatSingleEffect: x => format(x, 5, 5),
|
||||
formatEffect: x => format(10000 * x, 2, 2),
|
||||
formatSingleEffect: x => format(10000 * x, 2, 2),
|
||||
// It's bad to stack this one additively (N glyphs acts as a DT mult of N) or multiplicatively (the raw number is
|
||||
// less than 1), so instead we do a multiplicative stacking relative to the "base" effect of a level 1, 0% glyph.
|
||||
// We also introduce a 3x mult per glyph after the first, so that stacking level 1, 0% glyphs still has an effect.
|
||||
|
|
|
@ -146,6 +146,7 @@ GameDatabase.reality.imaginaryUpgrades = [
|
|||
name: "Recollection of Intrusion",
|
||||
id: 14,
|
||||
cost: 3.5e8,
|
||||
formatCost: x => format(x, 1),
|
||||
requirement: () => `Reach a tickspeed of ${format("1e75000000000")} / sec within Eternity Challenge 5`,
|
||||
hasFailed: () => false,
|
||||
checkRequirement: () => EternityChallenge(5).isRunning && Tickspeed.perSecond.exponent >= 7.5e10,
|
||||
|
@ -294,9 +295,10 @@ GameDatabase.reality.imaginaryUpgrades = [
|
|||
formatCost: x => format(x, 1),
|
||||
requirement: () => `Reach Reality in Lai'tela's Reality with all Dimensions disabled and
|
||||
at least ${formatInt(4)} empty Glyph slots`,
|
||||
hasFailed: () => !Laitela.isRunning || Laitela.maxAllowedDimension !== 0 || Glyphs.activeList.length > 1,
|
||||
hasFailed: () => !Laitela.isRunning || Laitela.maxAllowedDimension !== 0 ||
|
||||
Glyphs.activeWithoutCompanion.length > 1,
|
||||
checkRequirement: () => Laitela.isRunning && Laitela.maxAllowedDimension === 0 &&
|
||||
Glyphs.activeList.length <= 1 && TimeStudy.reality.isBought,
|
||||
Glyphs.activeWithoutCompanion.length <= 1 && TimeStudy.reality.isBought,
|
||||
checkEvent: GAME_EVENT.GAME_TICK_AFTER,
|
||||
description: "Unlock Pelle, Celestial of Antimatter",
|
||||
},
|
||||
|
|
|
@ -128,8 +128,7 @@ GameDatabase.reality.perks = {
|
|||
id: 40,
|
||||
label: "EU1",
|
||||
family: PERK_FAMILY.ETERNITY,
|
||||
description: `After the first Eternity of a Reality,
|
||||
automatically unlock the first row of Eternity Upgrades for free.`,
|
||||
description: `Automatically unlock the first row of Eternity Upgrades for free once you have Eternities.`,
|
||||
defaultPosition: new Vector(50, 150)
|
||||
},
|
||||
autounlockEU2: {
|
||||
|
@ -144,14 +143,14 @@ GameDatabase.reality.perks = {
|
|||
},
|
||||
autounlockDilation1: {
|
||||
id: 42,
|
||||
label: "UD1",
|
||||
label: "DU1",
|
||||
family: PERK_FAMILY.DILATION,
|
||||
description: "After unlocking Dilation, automatically unlock the second row of Dilation Upgrades for free.",
|
||||
defaultPosition: new Vector(165, 565)
|
||||
},
|
||||
autounlockDilation2: {
|
||||
id: 43,
|
||||
label: "UD2",
|
||||
label: "DU2",
|
||||
family: PERK_FAMILY.DILATION,
|
||||
description: "After unlocking Dilation, automatically unlock the third row of Dilation Upgrades for free.",
|
||||
defaultPosition: new Vector(310, 605)
|
||||
|
@ -257,7 +256,8 @@ GameDatabase.reality.perks = {
|
|||
label: "PEC2",
|
||||
family: PERK_FAMILY.AUTOMATION,
|
||||
get description() {
|
||||
return `Auto-complete one Eternity Challenge every ${formatInt(40)} minutes (real-time).`;
|
||||
return `Auto-complete one Eternity Challenge every ${formatInt(40)} minutes (real-time).
|
||||
(${formatInt(20)} minute decrease)`;
|
||||
},
|
||||
effect: 40,
|
||||
defaultPosition: new Vector(425, 235)
|
||||
|
@ -267,7 +267,8 @@ GameDatabase.reality.perks = {
|
|||
label: "PEC3",
|
||||
family: PERK_FAMILY.AUTOMATION,
|
||||
get description() {
|
||||
return `Auto-complete one Eternity Challenge every ${formatInt(20)} minutes (real-time).`;
|
||||
return `Auto-complete one Eternity Challenge every ${formatInt(20)} minutes (real-time).
|
||||
(${formatInt(20)} minute decrease)`;
|
||||
},
|
||||
effect: 20,
|
||||
automatorPoints: 10,
|
||||
|
@ -295,7 +296,7 @@ GameDatabase.reality.perks = {
|
|||
id: 72,
|
||||
label: "ECR",
|
||||
family: PERK_FAMILY.ETERNITY,
|
||||
description: "Remove non–Time Theorem requirements for unlocking Eternity Challenges.",
|
||||
description: "Remove non-Time Theorem requirements for unlocking Eternity Challenges.",
|
||||
automatorPoints: 10,
|
||||
shortDescription: () => "Remove EC secondary requirements",
|
||||
defaultPosition: new Vector(605, -160)
|
||||
|
@ -316,7 +317,7 @@ GameDatabase.reality.perks = {
|
|||
label: "TP1",
|
||||
family: PERK_FAMILY.DILATION,
|
||||
get description() {
|
||||
return `When buying the "You gain ${formatInt(3)} times more Tachyon Particles" Dilation Upgrade,
|
||||
return `When buying the 3rd rebuyable Dilation Upgrade,
|
||||
multiply your current Tachyon Particle amount by ${formatFloat(1.5, 1)}.`;
|
||||
},
|
||||
effect: 1.5,
|
||||
|
@ -327,7 +328,7 @@ GameDatabase.reality.perks = {
|
|||
label: "TP2",
|
||||
family: PERK_FAMILY.DILATION,
|
||||
get description() {
|
||||
return `When buying the "You gain ${formatInt(3)} times more Tachyon Particles" Dilation Upgrade,
|
||||
return `When buying the 3rd rebuyable Dilation Upgrade,
|
||||
multiply your current Tachyon Particle amount by ${formatInt(2)}.`;
|
||||
},
|
||||
effect: 2,
|
||||
|
@ -338,7 +339,7 @@ GameDatabase.reality.perks = {
|
|||
label: "TP3",
|
||||
family: PERK_FAMILY.DILATION,
|
||||
get description() {
|
||||
return `When buying the "You gain ${formatInt(3)} times more Tachyon Particles" Dilation Upgrade,
|
||||
return `When buying the 3rd rebuyable Dilation Upgrade,
|
||||
multiply your current Tachyon Particle amount by ${formatFloat(2.5, 1)}.`;
|
||||
},
|
||||
effect: 2.5,
|
||||
|
@ -349,7 +350,7 @@ GameDatabase.reality.perks = {
|
|||
label: "TP4",
|
||||
family: PERK_FAMILY.DILATION,
|
||||
get description() {
|
||||
return `When buying the "You gain ${formatInt(3)} times more Tachyon Particles" Dilation Upgrade,
|
||||
return `When buying the 3rd rebuyable Dilation Upgrade,
|
||||
multiply your current Tachyon Particle amount by ${formatInt(3)}.`;
|
||||
},
|
||||
effect: 3,
|
||||
|
@ -481,8 +482,10 @@ GameDatabase.reality.perks = {
|
|||
id: 205,
|
||||
label: "ACHNR",
|
||||
family: PERK_FAMILY.ACHIEVEMENT,
|
||||
description: "Reality no longer resets your Achievements.",
|
||||
effect: 2,
|
||||
get description() {
|
||||
return `Immediately unlock the first ${formatInt(13)} rows of Achievements
|
||||
and Reality no longer resets them.`;
|
||||
},
|
||||
automatorPoints: 10,
|
||||
shortDescription: () => "Keep Achievements on Reality",
|
||||
defaultPosition: new Vector(-195, -630)
|
||||
|
|
|
@ -71,7 +71,7 @@ GameDatabase.reality.upgrades = [
|
|||
name: "Cosmically Duplicate",
|
||||
id: 6,
|
||||
cost: 15,
|
||||
requirement: "Complete your first Eternity without using Replicanti Galaxies",
|
||||
requirement: "Complete your first manual Eternity without using Replicanti Galaxies",
|
||||
// Note that while noRG resets on eternity, the reality-level check will be false after the first eternity.
|
||||
// The noRG variable is eternity-level as it's also used for an achievement check
|
||||
hasFailed: () => !(player.requirementChecks.eternity.noRG && player.requirementChecks.reality.noEternities),
|
||||
|
@ -97,7 +97,7 @@ GameDatabase.reality.upgrades = [
|
|||
name: "Paradoxically Attain",
|
||||
id: 8,
|
||||
cost: 15,
|
||||
requirement: "Get to Eternity without any automatic Achievements",
|
||||
requirement: "Manually Eternity without any automatic Achievements",
|
||||
hasFailed: () => player.reality.gainedAutoAchievements,
|
||||
checkRequirement: () => !player.reality.gainedAutoAchievements,
|
||||
checkEvent: GAME_EVENT.ETERNITY_RESET_BEFORE,
|
||||
|
@ -110,15 +110,15 @@ GameDatabase.reality.upgrades = [
|
|||
id: 9,
|
||||
cost: 15,
|
||||
requirement: () => `Eternity for ${format("1e4000")} Eternity Points using
|
||||
only a single level ${formatInt(3)}+ Glyph.`,
|
||||
only a single Glyph which must be level ${formatInt(3)}+.`,
|
||||
hasFailed: () => {
|
||||
const invalidEquippedGlyphs = Glyphs.activeList.length > 1 ||
|
||||
(Glyphs.activeList.length === 1 && Glyphs.activeList[0].level < 3);
|
||||
const invalidEquippedGlyphs = Glyphs.activeWithoutCompanion.length > 1 ||
|
||||
(Glyphs.activeWithoutCompanion.length === 1 && Glyphs.activeWithoutCompanion[0].level < 3);
|
||||
const hasValidGlyphInInventory = Glyphs.inventory.countWhere(g => g && g.level >= 3) > 0;
|
||||
return invalidEquippedGlyphs || (Glyphs.activeList.length === 0 && !hasValidGlyphInInventory);
|
||||
return invalidEquippedGlyphs || (Glyphs.activeWithoutCompanion.length === 0 && !hasValidGlyphInInventory);
|
||||
},
|
||||
checkRequirement: () => Currency.eternityPoints.exponent >= 4000 &&
|
||||
Glyphs.activeList.length === 1 && Glyphs.activeList[0].level >= 3,
|
||||
Glyphs.activeWithoutCompanion.length === 1 && Glyphs.activeWithoutCompanion[0].level >= 3,
|
||||
checkEvent: GAME_EVENT.ETERNITY_RESET_AFTER,
|
||||
description: "Gain another Glyph slot",
|
||||
effect: () => 1
|
||||
|
@ -127,7 +127,7 @@ GameDatabase.reality.upgrades = [
|
|||
name: "Existentially Prolong",
|
||||
id: 10,
|
||||
cost: 15,
|
||||
requirement: () => `Complete your first Eternity with at least ${formatPostBreak(DC.E400)} Infinity Points`,
|
||||
requirement: () => `Complete your first manual Eternity with at least ${formatPostBreak(DC.E400)} Infinity Points`,
|
||||
hasFailed: () => !player.requirementChecks.reality.noEternities,
|
||||
checkRequirement: () => Currency.infinityPoints.exponent >= 400 &&
|
||||
player.requirementChecks.reality.noEternities,
|
||||
|
@ -168,13 +168,12 @@ GameDatabase.reality.upgrades = [
|
|||
name: "The Telemechanical Process",
|
||||
id: 13,
|
||||
cost: 50,
|
||||
requirement: () => `Eternity for ${format(DC.E4000)} Eternity Points without Time Dimensions 5-8`,
|
||||
requirement: () => `Eternity for ${format(DC.E4000)} Eternity Points without Time Dim. 5-8`,
|
||||
hasFailed: () => !Array.range(5, 4).every(i => TimeDimension(i).amount.equals(0)),
|
||||
checkRequirement: () => Currency.eternityPoints.exponent >= 4000 &&
|
||||
Array.range(5, 4).every(i => TimeDimension(i).amount.equals(0)),
|
||||
checkEvent: GAME_EVENT.ETERNITY_RESET_AFTER,
|
||||
description: () => `Unlock Time Dimension, ${formatX(5)} Eternity Point multiplier,
|
||||
and improved Eternity autobuyers`,
|
||||
description: () => `Improve Eternity Autobuyer and unlock autobuyers for Time Dimensions and ${formatX(5)} EP`,
|
||||
automatorPoints: 10,
|
||||
shortDescription: () => `TD and ${formatX(5)} EP Autobuyers, improved Eternity Autobuyer`,
|
||||
},
|
||||
|
@ -209,14 +208,14 @@ GameDatabase.reality.upgrades = [
|
|||
id: 16,
|
||||
cost: 1500,
|
||||
requirement: () => `Reality with ${formatInt(4)} Glyphs equipped of uncommon or better rarity
|
||||
(${formatInt(Glyphs.activeList.countWhere(g => g && g.strength >= 1.5))} equipped)`,
|
||||
(${formatInt(Glyphs.activeWithoutCompanion.countWhere(g => g && g.strength >= 1.5))} equipped)`,
|
||||
hasFailed: () => {
|
||||
const availableGlyphs = Glyphs.inventory.countWhere(g => g && g.strength >= 1.5);
|
||||
const equipped = Glyphs.activeList.countWhere(g => g.strength >= 1.5);
|
||||
const equipped = Glyphs.activeWithoutCompanion.countWhere(g => g.strength >= 1.5);
|
||||
const availableSlots = Glyphs.activeSlotCount - Glyphs.activeList.length;
|
||||
return equipped + Math.min(availableGlyphs, availableSlots) < 4;
|
||||
},
|
||||
checkRequirement: () => Glyphs.activeList.countWhere(g => g.strength >= 1.5) === 4,
|
||||
checkRequirement: () => Glyphs.activeWithoutCompanion.countWhere(g => g.strength >= 1.5) === 4,
|
||||
checkEvent: GAME_EVENT.REALITY_RESET_BEFORE,
|
||||
description: "Improve the Glyph rarity formula",
|
||||
effect: 1.3,
|
||||
|
@ -227,14 +226,15 @@ GameDatabase.reality.upgrades = [
|
|||
id: 17,
|
||||
cost: 1500,
|
||||
requirement: () => `Reality with ${formatInt(4)} Glyphs equipped, each having at least ${formatInt(2)} effects
|
||||
(${formatInt(Glyphs.activeList.countWhere(g => g && countValuesFromBitmask(g.effects) >= 2))} equipped)`,
|
||||
(${formatInt(Glyphs.activeWithoutCompanion.countWhere(g => g && countValuesFromBitmask(g.effects) >= 2))}
|
||||
equipped)`,
|
||||
hasFailed: () => {
|
||||
const availableGlyphs = Glyphs.inventory.countWhere(g => g && countValuesFromBitmask(g.effects) >= 2);
|
||||
const equipped = Glyphs.activeList.countWhere(g => countValuesFromBitmask(g.effects) >= 2);
|
||||
const equipped = Glyphs.activeWithoutCompanion.countWhere(g => countValuesFromBitmask(g.effects) >= 2);
|
||||
const availableSlots = Glyphs.activeSlotCount - Glyphs.activeList.length;
|
||||
return equipped + Math.min(availableGlyphs, availableSlots) < 4;
|
||||
},
|
||||
checkRequirement: () => Glyphs.activeList.countWhere(g => countValuesFromBitmask(g.effects) >= 2) === 4,
|
||||
checkRequirement: () => Glyphs.activeWithoutCompanion.countWhere(g => countValuesFromBitmask(g.effects) >= 2) === 4,
|
||||
checkEvent: GAME_EVENT.REALITY_RESET_BEFORE,
|
||||
description: () => `${formatPercents(0.5)} chance to get an additional effect on Glyphs`,
|
||||
effect: 0.5,
|
||||
|
@ -245,14 +245,14 @@ GameDatabase.reality.upgrades = [
|
|||
id: 18,
|
||||
cost: 1500,
|
||||
requirement: () => `Reality with ${formatInt(4)} Glyphs equipped, each at level ${formatInt(10)} or higher
|
||||
(${formatInt(Glyphs.activeList.countWhere(g => g && g.level >= 10))} equipped)`,
|
||||
(${formatInt(Glyphs.activeWithoutCompanion.countWhere(g => g && g.level >= 10))} equipped)`,
|
||||
hasFailed: () => {
|
||||
const availableGlyphs = Glyphs.inventory.countWhere(g => g && g.level >= 10);
|
||||
const equipped = Glyphs.activeList.countWhere(g => g.level >= 10);
|
||||
const equipped = Glyphs.activeWithoutCompanion.countWhere(g => g.level >= 10);
|
||||
const availableSlots = Glyphs.activeSlotCount - Glyphs.activeList.length;
|
||||
return equipped + Math.min(availableGlyphs, availableSlots) < 4;
|
||||
},
|
||||
checkRequirement: () => Glyphs.activeList.countWhere(g => g.level >= 10) === 4,
|
||||
checkRequirement: () => Glyphs.activeWithoutCompanion.countWhere(g => g.level >= 10) === 4,
|
||||
checkEvent: GAME_EVENT.REALITY_RESET_BEFORE,
|
||||
description: "Eternity count boosts Glyph level",
|
||||
effect: () => Math.max(Math.sqrt(Currency.eternities.value.plus(1).log10()) * 0.45, 1),
|
||||
|
@ -263,9 +263,9 @@ GameDatabase.reality.upgrades = [
|
|||
id: 19,
|
||||
cost: 1500,
|
||||
requirement: () => `Have a total of ${formatInt(30)} or more Glyphs at once
|
||||
(You have ${formatInt(Glyphs.allGlyphs.countWhere(g => g))})`,
|
||||
hasFailed: () => Glyphs.allGlyphs.countWhere(g => g) < 30,
|
||||
checkRequirement: () => Glyphs.allGlyphs.countWhere(g => g) >= 30,
|
||||
(You have ${formatInt(Glyphs.allGlyphs.countWhere(g => g.type !== "companion"))})`,
|
||||
hasFailed: () => Glyphs.allGlyphs.countWhere(g => g.type !== "companion") < 30,
|
||||
checkRequirement: () => Glyphs.allGlyphs.countWhere(g => g.type !== "companion") >= 30,
|
||||
checkEvent: GAME_EVENT.REALITY_RESET_BEFORE,
|
||||
description: "You can sacrifice Glyphs for permanent bonuses (Shift + click)",
|
||||
formatCost: value => format(value, 1, 0)
|
||||
|
@ -311,11 +311,12 @@ GameDatabase.reality.upgrades = [
|
|||
name: "Replicative Rapidity",
|
||||
id: 23,
|
||||
cost: 100000,
|
||||
requirement: () => `Reality in under ${formatInt(15)} minutes (Best: ${Time.bestReality.toStringShort()})`,
|
||||
requirement: () => `Reality in under ${formatInt(15)} minutes of game time
|
||||
(Fastest: ${Time.bestReality.toStringShort()})`,
|
||||
hasFailed: () => Time.thisReality.totalMinutes >= 15,
|
||||
checkRequirement: () => Time.thisReality.totalMinutes < 15,
|
||||
checkEvent: GAME_EVENT.REALITY_RESET_BEFORE,
|
||||
description: "Replicanti speed is boosted based on your fastest Reality",
|
||||
description: "Replicanti speed is boosted based on your fastest game-time Reality",
|
||||
effect: () => 15 / Math.clamp(Time.bestReality.totalMinutes, 1 / 12, 15),
|
||||
cap: 180,
|
||||
formatEffect: value => formatX(value, 2, 2)
|
||||
|
@ -324,9 +325,10 @@ GameDatabase.reality.upgrades = [
|
|||
name: "Synthetic Symbolism",
|
||||
id: 24,
|
||||
cost: 100000,
|
||||
requirement: () => `Reality for ${formatInt(5000)} Reality Machines without Glyphs`,
|
||||
hasFailed: () => Glyphs.activeList.length > 0,
|
||||
checkRequirement: () => MachineHandler.gainedRealityMachines.gte(5000) && Glyphs.activeList.length === 0,
|
||||
requirement: () => `Reality for ${formatInt(5000)} Reality Machines without equipped Glyphs`,
|
||||
hasFailed: () => Glyphs.activeWithoutCompanion.length > 0,
|
||||
checkRequirement: () => MachineHandler.gainedRealityMachines.gte(5000) &&
|
||||
Glyphs.activeWithoutCompanion.length === 0,
|
||||
checkEvent: GAME_EVENT.REALITY_RESET_BEFORE,
|
||||
description: "Gain another Glyph slot",
|
||||
effect: () => 1
|
||||
|
|
|
@ -6,7 +6,8 @@ GameDatabase.shopPurchases = {
|
|||
key: "dimPurchases",
|
||||
cost: 30,
|
||||
description: "Double all your Antimatter Dimension multipliers. Forever.",
|
||||
multiplier: purchases => Math.pow(2, purchases)
|
||||
multiplier: purchases => Math.pow(2, purchases),
|
||||
formatEffect: x => `×${Notation.scientific.formatDecimal(new Decimal(x))}`,
|
||||
},
|
||||
allDimPurchases: {
|
||||
key: "allDimPurchases",
|
||||
|
@ -18,12 +19,14 @@ GameDatabase.shopPurchases = {
|
|||
return `Double ALL Dimension multipliers (${makeEnumeration(dims)}; multiplicative until 32x). Forever.`;
|
||||
},
|
||||
multiplier: purchases => (purchases > 4 ? 32 + (purchases - 5) * 2 : Math.pow(2, purchases)),
|
||||
formatEffect: x => `×${x.toFixed(0)}`,
|
||||
},
|
||||
IPPurchases: {
|
||||
key: "IPPurchases",
|
||||
cost: 40,
|
||||
description: "Double your Infinity Point gain from all sources. (additive)",
|
||||
multiplier: purchases => (purchases === 0 ? 1 : 2 * purchases),
|
||||
formatEffect: x => `×${x.toFixed(0)}`,
|
||||
isUnlocked: () => PlayerProgress.infinityUnlocked(),
|
||||
lockText: "Infinity",
|
||||
},
|
||||
|
@ -32,7 +35,7 @@ GameDatabase.shopPurchases = {
|
|||
cost: 60,
|
||||
description: "Increase your Replicanti gain by 50%. (additive)",
|
||||
multiplier: purchases => (purchases === 0 ? 1 : 1 + 0.5 * purchases),
|
||||
formatEffect: x => formatX(x, 2, 1),
|
||||
formatEffect: x => `×${x.toFixed(1)}`,
|
||||
isUnlocked: () => Replicanti.areUnlocked || PlayerProgress.eternityUnlocked(),
|
||||
lockText: "Replicanti",
|
||||
},
|
||||
|
@ -41,6 +44,7 @@ GameDatabase.shopPurchases = {
|
|||
cost: 50,
|
||||
description: "Triple your Eternity Point gain from all sources. (additive)",
|
||||
multiplier: purchases => (purchases === 0 ? 1 : 3 * purchases),
|
||||
formatEffect: x => `×${x.toFixed(0)}`,
|
||||
isUnlocked: () => PlayerProgress.eternityUnlocked(),
|
||||
lockText: "Eternity",
|
||||
},
|
||||
|
@ -49,7 +53,7 @@ GameDatabase.shopPurchases = {
|
|||
cost: 40,
|
||||
description: "Increase your Dilated Time gain by 50%. (additive)",
|
||||
multiplier: purchases => (purchases === 0 ? 1 : 1 + 0.5 * purchases),
|
||||
formatEffect: x => formatX(x, 2, 1),
|
||||
formatEffect: x => `×${x.toFixed(1)}`,
|
||||
isUnlocked: () => PlayerProgress.dilationUnlocked() || PlayerProgress.realityUnlocked(),
|
||||
lockText: "Dilation",
|
||||
},
|
||||
|
@ -58,7 +62,7 @@ GameDatabase.shopPurchases = {
|
|||
cost: 60,
|
||||
description: "Increase your Reality Machine gain by 100%. (additive)",
|
||||
multiplier: purchases => purchases + 1,
|
||||
formatEffect: x => formatX(x, 2),
|
||||
formatEffect: x => `×${x.toFixed(0)}`,
|
||||
isUnlocked: () => PlayerProgress.realityUnlocked(),
|
||||
lockText: "Reality",
|
||||
},
|
||||
|
|
|
@ -13,6 +13,10 @@ GameDatabase.tabNotifications = {
|
|||
{
|
||||
parent: "challenges",
|
||||
tab: "normal"
|
||||
},
|
||||
{
|
||||
parent: "statistics",
|
||||
tab: "multipliers"
|
||||
}
|
||||
],
|
||||
condition: () => !PlayerProgress.realityUnlocked() &&
|
||||
|
|
|
@ -124,11 +124,14 @@ class ShopPurchaseState extends RebuyableMechanicState {
|
|||
return typeof cost === "function" ? cost() : cost;
|
||||
}
|
||||
|
||||
// ShopPurchaseData for any particular key is undefined in between page load and STD load,
|
||||
// so we need to guard against that causing NaNs to propagate through the save
|
||||
get purchases() {
|
||||
return ShopPurchaseData[this.config.key];
|
||||
return ShopPurchaseData[this.config.key] ?? 0;
|
||||
}
|
||||
|
||||
set purchases(value) {
|
||||
if (!Number.isFinite(value)) return;
|
||||
ShopPurchaseData[this.config.key] = value;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,8 +40,11 @@ export const Speedrun = {
|
|||
if (typeof player.options.animations[key] === "boolean") player.options.animations[key] = false;
|
||||
}
|
||||
|
||||
// "Fake News" Achievement, given for free to partially mitigate promoting weird strategies at the beginning of runs
|
||||
// A few achievements are given for free to mitigate weird strategies at the beginning of runs or unavoidable
|
||||
// timewalls for particularly fast/optimized runs
|
||||
Achievement(22).unlock();
|
||||
Achievement(35).unlock();
|
||||
Achievement(76).unlock();
|
||||
|
||||
// Some time elapses after the reset and before the UI is actually ready, which ends up getting "counted" as offline
|
||||
player.speedrun.offlineTimeUsed = 0;
|
||||
|
|
|
@ -43,12 +43,14 @@ export const Cloud = {
|
|||
try {
|
||||
await signInWithPopup(this.auth, this.provider);
|
||||
ShopPurchaseData.syncSTD();
|
||||
GameUI.notify.success(`Logged in as ${this.user.displayName}`);
|
||||
if (player.options.hideGoogleName) GameUI.notify.success(`Successfully logged in to Google Account`);
|
||||
else GameUI.notify.success(`Successfully logged in as ${this.user.displayName}`);
|
||||
} catch (e) {
|
||||
GameUI.notify.error("Google Account login failed");
|
||||
}
|
||||
},
|
||||
|
||||
// NOTE: This function is largely untested due to not being used at any place within web reality code
|
||||
async loadMobile() {
|
||||
if (!this.user) return;
|
||||
const snapshot = await get(ref(this.db, `users/${this.user.id}/player`));
|
||||
|
@ -80,23 +82,16 @@ export const Cloud = {
|
|||
},
|
||||
|
||||
async saveCheck(forceModal = false) {
|
||||
const save = await this.load();
|
||||
if (save === null) {
|
||||
const saveId = GameStorage.currentSlot;
|
||||
const cloudSave = await this.load();
|
||||
if (cloudSave === null) {
|
||||
this.save();
|
||||
} else {
|
||||
const root = GameSaveSerializer.deserialize(save);
|
||||
const saveId = GameStorage.currentSlot;
|
||||
const cloudSave = root.saves[saveId];
|
||||
const thisCloudHash = sha512_256(GameSaveSerializer.serialize(cloudSave));
|
||||
if (!this.lastCloudHash) this.lastCloudHash = thisCloudHash;
|
||||
const localSave = GameStorage.saves[saveId];
|
||||
const saveComparison = this.compareSaves(cloudSave, localSave, thisCloudHash);
|
||||
|
||||
// eslint-disable-next-line no-loop-func
|
||||
const overwriteAndSendCloudSave = () => {
|
||||
root.saves[saveId] = GameStorage.saves[saveId];
|
||||
this.save(saveId);
|
||||
};
|
||||
const overwriteAndSendCloudSave = () => this.save();
|
||||
|
||||
// If the comparison fails, we assume the cloud data is corrupted and show the relevant modal
|
||||
if (!saveComparison) {
|
||||
|
@ -119,37 +114,38 @@ export const Cloud = {
|
|||
}
|
||||
},
|
||||
|
||||
save(slot) {
|
||||
save() {
|
||||
if (!this.user) return;
|
||||
if (GlyphSelection.active || ui.$viewModel.modal.progressBar !== undefined) return;
|
||||
if (player.options.syncSaveIntervals) GameStorage.save();
|
||||
const root = {
|
||||
current: GameStorage.currentSlot,
|
||||
saves: GameStorage.saves,
|
||||
};
|
||||
const serializedSave = GameSaveSerializer.serialize(GameStorage.saves[GameStorage.currentSlot]);
|
||||
|
||||
this.lastCloudHash = sha512_256(GameSaveSerializer.serialize(root.saves[slot]));
|
||||
this.lastCloudHash = sha512_256(serializedSave);
|
||||
GameStorage.lastCloudSave = Date.now();
|
||||
GameIntervals.checkCloudSave.restart();
|
||||
set(ref(this.db, `users/${this.user.id}/web`), GameSaveSerializer.serialize(root));
|
||||
GameUI.notify.info(`Game saved (slot ${slot + 1}) to cloud with user ${this.user.displayName}`);
|
||||
|
||||
const slot = GameStorage.currentSlot;
|
||||
this.writeToCloudDB(slot, serializedSave);
|
||||
if (player.options.hideGoogleName) GameUI.notify.info(`Game saved (slot ${slot + 1}) to cloud`);
|
||||
else GameUI.notify.info(`Game saved (slot ${slot + 1}) to cloud as user ${this.user.displayName}`);
|
||||
},
|
||||
|
||||
async loadCheck() {
|
||||
const save = await this.load();
|
||||
if (save === null) {
|
||||
GameUI.notify.info(`No cloud save for user ${this.user.displayName}`);
|
||||
if (player.options.hideGoogleName) GameUI.notify.info(`No cloud save for current Google Account`);
|
||||
else GameUI.notify.info(`No cloud save for user ${this.user.displayName}`);
|
||||
} else {
|
||||
const root = GameSaveSerializer.deserialize(save);
|
||||
const cloudSave = save;
|
||||
const saveId = GameStorage.currentSlot;
|
||||
const cloudSave = root.saves[saveId];
|
||||
const localSave = GameStorage.saves[saveId];
|
||||
const saveComparison = this.compareSaves(cloudSave, localSave);
|
||||
|
||||
// eslint-disable-next-line no-loop-func
|
||||
const overwriteLocalSave = () => {
|
||||
GameStorage.overwriteSlot(saveId, cloudSave);
|
||||
GameUI.notify.info(`Cloud save (slot ${saveId + 1}) loaded for user ${this.user.displayName}`);
|
||||
if (player.options.hideGoogleName) GameUI.notify.info(`Cloud save (slot ${saveId + 1}) loaded`);
|
||||
else GameUI.notify.info(`Cloud save (slot ${saveId + 1}) loaded for user ${this.user.displayName}`);
|
||||
};
|
||||
|
||||
// If the comparison fails, we assume the cloud data is corrupted and show the relevant modal
|
||||
|
@ -173,12 +169,42 @@ export const Cloud = {
|
|||
},
|
||||
|
||||
async load() {
|
||||
const snapshot = await get(ref(this.db, `users/${this.user.id}/web`));
|
||||
if (snapshot.exists) return snapshot.val();
|
||||
let singleSlot = await this.readFromCloudDB(GameStorage.currentSlot);
|
||||
if (singleSlot.exists()) return GameSaveSerializer.deserialize(singleSlot.val());
|
||||
|
||||
// If it doesn't exist, we assume that the cloud save hasn't been migrated yet and apply the migration before
|
||||
// trying again. If it still doesn't exist, the cloud save was actually empty and there was nothing to migrate
|
||||
await this.separateSaveSlots(combinedSlots.val());
|
||||
singleSlot = await this.readFromCloudDB(GameStorage.currentSlot);
|
||||
if (singleSlot.exists()) return GameSaveSerializer.deserialize(singleSlot.val());
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
// The initial implementation of cloud saving combined all save files in the same DB entry, but we have since changed
|
||||
// it so that they're all saved in separate slots. The database itself retains the single-entry data until the first
|
||||
// player load attempt after this change, at which point this is called client-side to do a one-time format migration
|
||||
// Before the migration, saves were stored in ".../web" and afterward they have been moved to ".../web/1" and similar
|
||||
async separateSaveSlots(oldData) {
|
||||
const allData = GameSaveSerializer.deserialize(oldData);
|
||||
if (!allData) return;
|
||||
|
||||
for (const slot of Object.keys(allData.saves)) {
|
||||
const newData = GameSaveSerializer.serialize(allData.saves[slot]);
|
||||
await this.writeToCloudDB(Number(slot), newData);
|
||||
}
|
||||
},
|
||||
|
||||
readFromCloudDB(slot) {
|
||||
const slotStr = slot === null ? "" : `/${slot}`;
|
||||
return get(ref(this.db, `users/${this.user.id}/web${slotStr}`));
|
||||
},
|
||||
|
||||
writeToCloudDB(slot, data) {
|
||||
const slotStr = slot === null ? "" : `/${slot}`;
|
||||
return set(ref(this.db, `users/${this.user.id}/web${slotStr}`), data);
|
||||
},
|
||||
|
||||
logout() {
|
||||
signOut(this.auth);
|
||||
ShopPurchaseData.clearLocalSTD();
|
||||
|
|
|
@ -157,6 +157,72 @@ GameStorage.migrations = {
|
|||
GameStorage.migrations.moveTS33(player);
|
||||
GameStorage.migrations.addBestPrestigeCurrency(player);
|
||||
GameStorage.migrations.migrateTheme(player);
|
||||
},
|
||||
14: player => {
|
||||
GameStorage.migrations.reworkBHPulsing(player);
|
||||
|
||||
// Added glyph auto-sort by level; in order to keep the button state cycling consistent with the sort buttons' UI
|
||||
// order, AUTO_SORT_MODE had to be changed to insert LEVEL mode at the top and shift the others down. This
|
||||
// makes sure that older saves maintain the same settings after this shift
|
||||
if (player.reality.autoSort !== 0) player.reality.autoSort++;
|
||||
},
|
||||
15: player => {
|
||||
// Added additional resource tracking in last 10 prestige records and adjusted data format to be more consistent
|
||||
// by reordering to be [game time, real time, prestige currency, prestige count, challenge, ...(other resources)]
|
||||
// Also fixes a migration bug where values could be undefined or null by assigning defaults when necessary
|
||||
for (let i = 0; i < 10; i++) {
|
||||
if (player.records.lastTenInfinities) {
|
||||
const infRec = player.records.lastTenInfinities[i];
|
||||
player.records.recentInfinities[i] = [
|
||||
infRec[0] ?? Number.MAX_VALUE,
|
||||
Number(infRec[3] ?? Number.MAX_VALUE),
|
||||
new Decimal(infRec[1] ?? 1),
|
||||
new Decimal(infRec[2] ?? 1),
|
||||
""
|
||||
];
|
||||
}
|
||||
|
||||
if (player.records.lastTenEternities) {
|
||||
const eterRec = player.records.lastTenEternities[i];
|
||||
player.records.recentEternities[i] = [
|
||||
eterRec[0] ?? Number.MAX_VALUE,
|
||||
Number(eterRec[3] ?? Number.MAX_VALUE),
|
||||
new Decimal(eterRec[1] ?? 1),
|
||||
new Decimal(eterRec[2] ?? 1),
|
||||
"",
|
||||
new Decimal(0)
|
||||
];
|
||||
}
|
||||
|
||||
if (player.records.lastTenRealities) {
|
||||
const realRec = player.records.lastTenRealities[i];
|
||||
player.records.recentRealities[i] = [
|
||||
realRec[0] ?? Number.MAX_VALUE,
|
||||
Number(realRec[3] ?? Number.MAX_VALUE),
|
||||
new Decimal(realRec[1] ?? 1),
|
||||
realRec[2] ?? 1,
|
||||
"",
|
||||
0,
|
||||
0
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
delete player.records.lastTenInfinities;
|
||||
delete player.records.lastTenEternities;
|
||||
delete player.records.lastTenRealities;
|
||||
delete player.options.showLastTenResourceGain;
|
||||
|
||||
// Fixes a desync which occasionally causes unique > total seen due to total not being updated properly
|
||||
if (player.news.seen) {
|
||||
let unique = 0;
|
||||
for (const bitmaskArray of Object.values(player.news.seen)) {
|
||||
for (const bitmask of bitmaskArray) {
|
||||
unique += countValuesFromBitmask(bitmask);
|
||||
}
|
||||
}
|
||||
player.news.totalSeen = Math.max(player.news.totalSeen, unique);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -941,6 +1007,11 @@ GameStorage.migrations = {
|
|||
delete player.options.secretThemeKey;
|
||||
},
|
||||
|
||||
// This change removed the ability to adjust stored time rate after Ra-Nameless 10, instead forcing it to be 99%
|
||||
reworkBHPulsing(player) {
|
||||
delete player.celestials.enslaved.storedFraction;
|
||||
},
|
||||
|
||||
prePatch(saveData) {
|
||||
// Initialize all possibly undefined properties that were not present in
|
||||
// previous versions and which could be overwritten by deepmerge
|
||||
|
|
|
@ -17,6 +17,10 @@ export const GameStorage = {
|
|||
offlineEnabled: undefined,
|
||||
offlineTicks: undefined,
|
||||
|
||||
maxOfflineTicks(simulatedMs, defaultTicks = this.offlineTicks) {
|
||||
return Math.clampMax(defaultTicks, Math.floor(simulatedMs / 50));
|
||||
},
|
||||
|
||||
get localStorageKey() {
|
||||
return DEV ? "dimensionTestSave" : "dimensionSave";
|
||||
},
|
||||
|
@ -226,7 +230,7 @@ export const GameStorage = {
|
|||
// Needed to check some notification about reality unlock study.
|
||||
EventHub.dispatch(GAME_EVENT.SAVE_CONVERTED_FROM_PREVIOUS_VERSION);
|
||||
}
|
||||
if (DEV || player.options.testVersion !== undefined) {
|
||||
if (DEV && player.options.testVersion !== undefined) {
|
||||
this.devMigrations.patch(player);
|
||||
}
|
||||
}
|
||||
|
@ -249,6 +253,7 @@ export const GameStorage = {
|
|||
Enslaved.boostReality = false;
|
||||
GameEnd.additionalEnd = 0;
|
||||
Theme.set(Theme.currentName());
|
||||
Glyphs.unseen = [];
|
||||
Notations.find(player.options.notation).setAsCurrent(true);
|
||||
ADNotations.Settings.exponentCommas.show = player.options.commas;
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ export function buyMaxTickSpeed() {
|
|||
let boughtTickspeed = false;
|
||||
|
||||
Tutorial.turnOffEffect(TUTORIAL_STATE.TICKSPEED);
|
||||
if (NormalChallenge(9).isRunning || InfinityChallenge(5).isRunning) {
|
||||
if (NormalChallenge(9).isRunning) {
|
||||
const goal = Player.infinityGoal;
|
||||
let cost = Tickspeed.cost;
|
||||
while (Currency.antimatter.gt(cost) && cost.lt(goal)) {
|
||||
|
@ -127,7 +127,6 @@ export const Tickspeed = {
|
|||
|
||||
get isAvailableForPurchase() {
|
||||
return this.isUnlocked &&
|
||||
Currency.antimatter.lt(Player.infinityLimit) &&
|
||||
!EternityChallenge(9).isRunning &&
|
||||
!Laitela.continuumActive &&
|
||||
(player.break || this.cost.lt(Decimal.NUMBER_MAX_VALUE));
|
||||
|
|
|
@ -71,6 +71,16 @@ function findLastOpenSubtab(tabId, subtabs) {
|
|||
return subtabs.find(s => s.id === player.options.lastOpenSubtab[tabId]) ?? subtabs[0];
|
||||
}
|
||||
|
||||
function cycleThroughSubtabs(subtabs, currentSubtab) {
|
||||
const availableTabs = subtabs.filter(tab => tab.isAvailable);
|
||||
const currentIndex = availableTabs.indexOf(currentSubtab);
|
||||
const direction = ui.view.shiftDown ? -1 : 1;
|
||||
let newIndex = currentIndex + direction;
|
||||
newIndex = newIndex < 0 ? availableTabs.length - 1 : newIndex;
|
||||
newIndex = newIndex > availableTabs.length - 1 ? 0 : newIndex;
|
||||
return availableTabs[newIndex];
|
||||
}
|
||||
|
||||
class TabState {
|
||||
constructor(config) {
|
||||
this.config = config;
|
||||
|
@ -129,17 +139,19 @@ class TabState {
|
|||
|
||||
show(manual, subtab = undefined) {
|
||||
if (!manual && !player.options.automaticTabSwitching || Quote.isOpen) return;
|
||||
ui.view.tab = this.key;
|
||||
if (subtab === undefined) {
|
||||
this._currentSubtab = findLastOpenSubtab(this.id, this.subtabs);
|
||||
} else {
|
||||
if (subtab !== undefined) {
|
||||
if (!Enslaved.isRunning) subtab.unhideTab();
|
||||
this._currentSubtab = subtab;
|
||||
} else if (ui.view.tab === this.key && ui.view.initialized) {
|
||||
this._currentSubtab = cycleThroughSubtabs(this.subtabs, this._currentSubtab);
|
||||
} else {
|
||||
this._currentSubtab = findLastOpenSubtab(this.id, this.subtabs);
|
||||
}
|
||||
|
||||
if (!this._currentSubtab.isUnlocked) this.resetToUnlocked();
|
||||
if (!this._currentSubtab.isAvailable) this.resetToAvailable();
|
||||
|
||||
ui.view.tab = this.key;
|
||||
ui.view.subtab = this._currentSubtab.key;
|
||||
const tabNotificationKey = this.key + this._currentSubtab.key;
|
||||
if (player.tabNotifications.has(tabNotificationKey)) player.tabNotifications.delete(tabNotificationKey);
|
||||
|
|
|
@ -2,11 +2,11 @@ import TWEEN from "tween.js";
|
|||
|
||||
import { DC } from "./core/constants";
|
||||
import { deepmergeAll } from "@/utility/deepmerge";
|
||||
import { DEV } from "./core/devtools";
|
||||
import { SpeedrunMilestones } from "./core/speedrun";
|
||||
import { supportedBrowsers } from "./supported-browsers";
|
||||
|
||||
import Payments from "./core/payments";
|
||||
import { DEV } from "./core/devtools";
|
||||
|
||||
if (GlobalErrorHandler.handled) {
|
||||
throw new Error("Initialization failed");
|
||||
|
@ -75,6 +75,8 @@ export function breakInfinity() {
|
|||
for (const autobuyer of Autobuyers.all) {
|
||||
if (autobuyer.data.interval !== undefined) autobuyer.maxIntervalForFree();
|
||||
}
|
||||
// There's a potential migration edge case involving already-maxed autobuyers; this should give the achievement
|
||||
Achievement(61).tryUnlock();
|
||||
player.break = !player.break;
|
||||
TabNotification.ICUnlock.tryTrigger();
|
||||
EventHub.dispatch(player.break ? GAME_EVENT.BREAK_INFINITY : GAME_EVENT.FIX_INFINITY);
|
||||
|
@ -177,42 +179,20 @@ export function ratePerMinute(amount, time) {
|
|||
return Decimal.divide(amount, time / (60 * 1000));
|
||||
}
|
||||
|
||||
export function averageRun(allRuns, name) {
|
||||
// Filter out all runs which have the default infinite value for time, but if we're left with no valid runs then we
|
||||
// take just one entry so that the averages also have the same value and we don't get division by zero.
|
||||
let runs = allRuns.filter(run => run[0] !== Number.MAX_VALUE);
|
||||
if (runs.length === 0) runs = [allRuns[0]];
|
||||
const totalTime = runs.map(run => run[0]).sum();
|
||||
const totalAmount = runs
|
||||
.map(run => run[1])
|
||||
.reduce(Decimal.sumReducer);
|
||||
const totalPrestigeGain = runs
|
||||
.map(run => run[2])
|
||||
.reduce(name === "Reality" ? Number.sumReducer : Decimal.sumReducer);
|
||||
const realTime = runs.map(run => run[3]).sum();
|
||||
const average = [
|
||||
totalTime / runs.length,
|
||||
totalAmount.dividedBy(runs.length),
|
||||
(name === "Reality") ? totalPrestigeGain / runs.length : totalPrestigeGain.dividedBy(runs.length),
|
||||
realTime / runs.length
|
||||
];
|
||||
if (name === "Reality") {
|
||||
average.push(runs.map(x => x[4]).sum() / runs.length);
|
||||
}
|
||||
return average;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-params
|
||||
export function addInfinityTime(time, realTime, ip, infinities) {
|
||||
player.records.lastTenInfinities.pop();
|
||||
player.records.lastTenInfinities.unshift([time, ip, infinities, realTime]);
|
||||
let challenge = "";
|
||||
if (player.challenge.normal.current) challenge = `Normal Challenge ${player.challenge.normal.current}`;
|
||||
if (player.challenge.infinity.current) challenge = `Infinity Challenge ${player.challenge.infinity.current}`;
|
||||
player.records.recentInfinities.pop();
|
||||
player.records.recentInfinities.unshift([time, realTime, ip, infinities, challenge]);
|
||||
GameCache.bestRunIPPM.invalidate();
|
||||
}
|
||||
|
||||
export function resetInfinityRuns() {
|
||||
player.records.lastTenInfinities = Array.from(
|
||||
player.records.recentInfinities = Array.from(
|
||||
{ length: 10 },
|
||||
() => [Number.MAX_VALUE, DC.D1, DC.D1, Number.MAX_VALUE]
|
||||
() => [Number.MAX_VALUE, Number.MAX_VALUE, DC.D1, DC.D1, ""]
|
||||
);
|
||||
GameCache.bestRunIPPM.invalidate();
|
||||
}
|
||||
|
@ -227,15 +207,24 @@ export function getInfinitiedMilestoneReward(ms, considerMilestoneReached) {
|
|||
|
||||
// eslint-disable-next-line max-params
|
||||
export function addEternityTime(time, realTime, ep, eternities) {
|
||||
player.records.lastTenEternities.pop();
|
||||
player.records.lastTenEternities.unshift([time, ep, eternities, realTime]);
|
||||
let challenge = "";
|
||||
if (player.challenge.eternity.current) {
|
||||
const currEC = player.challenge.eternity.current;
|
||||
const ec = EternityChallenge(currEC);
|
||||
const challText = player.dilation.active ? "Dilated EC" : "Eternity Challenge";
|
||||
challenge = `${challText} ${currEC} (${formatInt(ec.completions)}/${formatInt(ec.maxCompletions)})`;
|
||||
} else if (player.dilation.active) challenge = "Time Dilation";
|
||||
// If we call this function outside of dilation, it uses the existing AM and produces an erroneous number
|
||||
const gainedTP = player.dilation.active ? getTachyonGain() : DC.D0;
|
||||
player.records.recentEternities.pop();
|
||||
player.records.recentEternities.unshift([time, realTime, ep, eternities, challenge, gainedTP]);
|
||||
GameCache.averageRealTimePerEternity.invalidate();
|
||||
}
|
||||
|
||||
export function resetEternityRuns() {
|
||||
player.records.lastTenEternities = Array.from(
|
||||
player.records.recentEternities = Array.from(
|
||||
{ length: 10 },
|
||||
() => [Number.MAX_VALUE, DC.D1, DC.D1, Number.MAX_VALUE]
|
||||
() => [Number.MAX_VALUE, Number.MAX_VALUE, DC.D1, DC.D1, "", DC.D0]
|
||||
);
|
||||
GameCache.averageRealTimePerEternity.invalidate();
|
||||
}
|
||||
|
@ -260,8 +249,14 @@ export function getOfflineEPGain(ms) {
|
|||
|
||||
// eslint-disable-next-line max-params
|
||||
export function addRealityTime(time, realTime, rm, level, realities) {
|
||||
player.records.lastTenRealities.pop();
|
||||
player.records.lastTenRealities.unshift([time, rm, realities, realTime, level]);
|
||||
let reality = "";
|
||||
const celestials = [Teresa, Effarig, Enslaved, V, Ra, Laitela];
|
||||
for (const cel of celestials) {
|
||||
if (cel.isRunning) reality = cel.displayName;
|
||||
}
|
||||
const shards = Effarig.shardsGained;
|
||||
player.records.recentRealities.pop();
|
||||
player.records.recentRealities.unshift([time, realTime, rm, realities, reality, level, shards]);
|
||||
}
|
||||
|
||||
export function gainedInfinities() {
|
||||
|
@ -346,9 +341,8 @@ export function getGameSpeedupFactor(effectsToConsider, blackHolesActiveOverride
|
|||
factor = Math.pow(factor, getAdjustedGlyphEffect("effarigblackhole"));
|
||||
}
|
||||
|
||||
// Time storage is linearly scaled because exponential scaling is pretty useless in practice
|
||||
if (Enslaved.isStoringGameTime && effects.includes(GAME_SPEED_EFFECT.TIME_STORAGE)) {
|
||||
const storedTimeWeight = player.celestials.enslaved.storedFraction;
|
||||
const storedTimeWeight = Ra.unlocks.autoPulseTime.canBeApplied ? 0.99 : 1;
|
||||
factor = factor * (1 - storedTimeWeight) + storedTimeWeight;
|
||||
}
|
||||
|
||||
|
@ -534,7 +528,7 @@ export function gameLoop(passDiff, options = {}) {
|
|||
Currency.realities.add(uncountabilityGain);
|
||||
Currency.perkPoints.add(uncountabilityGain);
|
||||
|
||||
if (Perk.autocompleteEC1.canBeApplied && player.reality.autoEC) player.reality.lastAutoEC += realDiff;
|
||||
if (Perk.autocompleteEC1.canBeApplied) player.reality.lastAutoEC += realDiff;
|
||||
|
||||
EternityChallenge(12).tryFail();
|
||||
Achievements._power.invalidate();
|
||||
|
@ -546,21 +540,13 @@ export function gameLoop(passDiff, options = {}) {
|
|||
const gain = Math.clampMin(FreeTickspeed.fromShards(Currency.timeShards.value).newAmount - player.totalTickGained, 0);
|
||||
player.totalTickGained += gain;
|
||||
|
||||
const currentIPmin = gainedInfinityPoints().dividedBy(Math.clampMin(0.0005, Time.thisInfinityRealTime.totalMinutes));
|
||||
if (currentIPmin.gt(player.records.thisInfinity.bestIPmin) && Player.canCrunch)
|
||||
player.records.thisInfinity.bestIPmin = currentIPmin;
|
||||
|
||||
updatePrestigeRates();
|
||||
tryCompleteInfinityChallenges();
|
||||
|
||||
EternityChallenges.autoComplete.tick();
|
||||
|
||||
replicantiLoop(diff);
|
||||
|
||||
|
||||
const currentEPmin = gainedEternityPoints().dividedBy(Math.clampMin(0.0005, Time.thisEternityRealTime.totalMinutes));
|
||||
if (currentEPmin.gt(player.records.thisEternity.bestEPmin) && Player.canEternity)
|
||||
player.records.thisEternity.bestEPmin = currentEPmin;
|
||||
|
||||
if (PlayerProgress.dilationUnlocked()) {
|
||||
Currency.dilatedTime.add(getDilationGainPerSecond().times(diff / 1000));
|
||||
}
|
||||
|
@ -591,8 +577,8 @@ export function gameLoop(passDiff, options = {}) {
|
|||
const teresa25 = !isInCelestialReality() && Ra.unlocks.unlockDilationStartingTP.canBeApplied;
|
||||
if ((teresa1 || teresa25) && !Pelle.isDoomed) rewardTP();
|
||||
|
||||
if (!EnslavedProgress.hintsUnlocked.hasProgress && Enslaved.has(ENSLAVED_UNLOCKS.RUN) && !Enslaved.isCompleted) {
|
||||
player.celestials.enslaved.hintUnlockProgress += Enslaved.isRunning ? realDiff : realDiff / 25;
|
||||
if (Enslaved.canTickHintTimer) {
|
||||
player.celestials.enslaved.hintUnlockProgress += Enslaved.isRunning ? realDiff : (realDiff * 0.4);
|
||||
if (player.celestials.enslaved.hintUnlockProgress >= TimeSpan.fromHours(5).totalMilliseconds) {
|
||||
EnslavedProgress.hintsUnlocked.giveProgress();
|
||||
Enslaved.quotes.hintUnlock.show();
|
||||
|
@ -630,6 +616,26 @@ export function gameLoop(passDiff, options = {}) {
|
|||
PerformanceStats.end("Game Update");
|
||||
}
|
||||
|
||||
function updatePrestigeRates() {
|
||||
const currentIPmin = gainedInfinityPoints().dividedBy(Math.clampMin(0.0005, Time.thisInfinityRealTime.totalMinutes));
|
||||
if (currentIPmin.gt(player.records.thisInfinity.bestIPmin) && Player.canCrunch) {
|
||||
player.records.thisInfinity.bestIPmin = currentIPmin;
|
||||
player.records.thisInfinity.bestIPminVal = gainedInfinityPoints();
|
||||
}
|
||||
|
||||
const currentEPmin = gainedEternityPoints().dividedBy(Math.clampMin(0.0005, Time.thisEternityRealTime.totalMinutes));
|
||||
if (currentEPmin.gt(player.records.thisEternity.bestEPmin) && Player.canEternity) {
|
||||
player.records.thisEternity.bestEPmin = currentEPmin;
|
||||
player.records.thisEternity.bestEPminVal = gainedEternityPoints();
|
||||
}
|
||||
|
||||
const currentRSmin = Effarig.shardsGained / Math.clampMin(0.0005, Time.thisRealityRealTime.totalMinutes);
|
||||
if (currentRSmin > player.records.thisReality.bestRSmin && isRealityAvailable()) {
|
||||
player.records.thisReality.bestRSmin = currentRSmin;
|
||||
player.records.thisReality.bestRSminVal = Effarig.shardsGained;
|
||||
}
|
||||
}
|
||||
|
||||
function passivePrestigeGen() {
|
||||
let eternitiedGain = 0;
|
||||
if (RealityUpgrade(14).isBought) {
|
||||
|
@ -650,7 +656,7 @@ function passivePrestigeGen() {
|
|||
let infGen = DC.D0;
|
||||
if (BreakInfinityUpgrade.infinitiedGen.isBought) {
|
||||
// Multipliers are done this way to explicitly exclude ach87 and TS32
|
||||
infGen = infGen.plus(0.2 * Time.deltaTimeMs / Math.clampMin(33, player.records.bestInfinity.time));
|
||||
infGen = infGen.plus(0.5 * Time.deltaTimeMs / Math.clampMin(50, player.records.bestInfinity.time));
|
||||
infGen = infGen.timesEffectsOf(
|
||||
RealityUpgrade(5),
|
||||
RealityUpgrade(7),
|
||||
|
@ -683,12 +689,7 @@ function applyAutoUnlockPerks() {
|
|||
}
|
||||
if (Perk.autounlockDilation3.canBeApplied) buyDilationUpgrade(DilationUpgrade.ttGenerator.id);
|
||||
if (Perk.autounlockReality.canBeApplied) TimeStudy.reality.purchase(true);
|
||||
if (player.eternityUpgrades.size < 6 && Perk.autounlockEU2.canBeApplied) {
|
||||
const secondRow = EternityUpgrade.all.filter(u => u.id > 3);
|
||||
for (const upgrade of secondRow) {
|
||||
if (player.eternityPoints.gte(upgrade.cost / 1e10)) player.eternityUpgrades.add(upgrade.id);
|
||||
}
|
||||
}
|
||||
applyEU2();
|
||||
}
|
||||
|
||||
function laitelaRealityTick(realDiff) {
|
||||
|
@ -872,7 +873,7 @@ export function simulateTime(seconds, real, fast) {
|
|||
GameUI.notify.showBlackHoles = false;
|
||||
|
||||
// Limit the tick count (this also applies if the black hole is unlocked)
|
||||
const maxTicks = GameStorage.offlineTicks ?? player.options.offlineTicks;
|
||||
const maxTicks = GameStorage.maxOfflineTicks(1000 * seconds, GameStorage.offlineTicks ?? player.options.offlineTicks);
|
||||
if (ticks > maxTicks && !real && !fast) {
|
||||
ticks = maxTicks;
|
||||
} else if (ticks > 50 && !real && fast) {
|
||||
|
|
BIN
public/audio/s12-startup.mp3
Normal file
BIN
public/images/s12-bg.jpg
Normal file
After Width: | Height: | Size: 628 KiB |
BIN
public/images/s12/achievements.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
public/images/s12/automation.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
public/images/s12/celestials.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
public/images/s12/challenges.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
public/images/s12/desktop--discord-logo.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
public/images/s12/desktop--games.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
public/images/s12/desktop--windows-media-player.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
public/images/s12/dimensions.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
public/images/s12/eternity.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
public/images/s12/game--alkahistorian.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
public/images/s12/game--anti-idle.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
public/images/s12/game--hex-game.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
1
public/images/s12/game--melvor-idle.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 465.39 513.15"><defs><style>.cls-1{fill:#bd1818;}.cls-2{fill:#e52320;}.cls-3{fill:#dc9f12;}.cls-4{fill:#f4df61;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><polygon class="cls-1" points="20.21 115.95 32.02 124.19 32.02 135.2 20.21 139.25 20.21 115.95"/><polygon class="cls-2" points="349.47 473.5 319.37 451.96 319.37 120.21 349.47 106.41 349.47 473.5"/><polygon class="cls-1" points="64.88 101.04 76.69 109.47 76.69 119.87 64.88 123.92 64.88 101.04"/><polygon class="cls-2" points="349.47 84.33 319.37 105.56 319.37 120.21 349.47 120.05 349.47 84.33"/><polygon class="cls-2" points="445.18 115.95 433.38 123.92 433.38 135.2 445.18 139.25 445.18 115.95"/><polygon class="cls-2" points="400.51 101.04 388.7 109.19 388.7 119.87 400.51 123.92 400.51 101.04"/><polygon class="cls-2" points="349.47 148.97 319.44 148.97 232.73 209.23 232.73 233.27 349.47 148.97"/><polygon class="cls-1" points="146.02 148.97 145.87 148.98 145.87 120.21 145.87 106.17 115.91 84.33 115.91 106.41 115.91 120.05 115.91 473.5 145.87 452.03 145.87 171.27 232.73 233.27 232.73 209.23 146.02 148.97"/><polygon class="cls-2" points="232.73 233.27 115.92 149.89 115.92 120.05 115.92 120.05 115.92 84.33 85.82 93.64 85.82 116.74 64.88 123.92 64.88 101.04 41.37 108.9 41.37 131.99 20.21 139.25 20.21 115.95 0 122.66 0 146.19 0 146.19 0 205.18 26.54 224.19 26.54 458.69 115.92 473.5 115.92 310.95 232.73 399.41 232.73 233.27"/><polygon class="cls-1" points="445.18 115.95 445.18 139.25 424.02 131.99 424.02 108.89 400.51 101.04 400.51 123.92 379.57 116.74 379.57 93.64 349.47 84.33 349.47 106.41 349.47 120.05 349.47 148.97 232.73 233.27 232.73 399.41 349.47 309.54 349.47 473.5 438.17 462.82 438.17 224.19 465.39 205.16 465.39 148.97 465.39 146.19 465.39 122.66 445.18 115.95"/><path class="cls-3" d="M232.7,416.27h0v96.89l.8-1.1,31.12-44.37c-4.5-8.09-6.84-40.89-8.06-70.72Z"/><path class="cls-3" d="M241.18,23.83l5.26,60,40.82,0,14.06,14.64-16.44,15.93-10.69-11.06h-8.71c-4.14,4.34-7.3,9.8-7.53,16.46v54.11L232.7,193.84h0V0a12.73,12.73,0,0,1,11.65,12.66Z"/><path class="cls-4" d="M232.68,416.27h0v96.89l-.81-1.1-31.11-44.37c4.49-8.09,6.84-40.89,8-70.72Z"/><path class="cls-4" d="M224,23.83l-5.07,60-40.82,0L164.07,98.48l16.44,15.93,10.69-11.06h8.71c4.14,4.34,7.3,9.8,7.53,16.46v54.11l25.25,19.92h0V0a12.73,12.73,0,0,0-11.64,12.66Z"/></g></g></svg>
|
After Width: | Height: | Size: 2.4 KiB |
BIN
public/images/s12/game--mine-defense.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
public/images/s12/game--monies2.png
Normal file
After Width: | Height: | Size: 766 B |
BIN
public/images/s12/game--synergism.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
public/images/s12/game--trimps.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
public/images/s12/game--universal-paperclips.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
public/images/s12/game--wami.png
Normal file
After Width: | Height: | Size: 24 KiB |