mirror of
https://github.com/IvarK/AntimatterDimensionsSourceCode.git
synced 2024-11-22 04:05:42 +00:00
Merge branch 'master' into Steam-Webpack
This commit is contained in:
commit
5757bc7736
@ -55,7 +55,6 @@ import GlyphSetSaveDeleteModal from "@/components/modals/GlyphSetSaveDeleteModal
|
||||
import GlyphShowcasePanelModal from "@/components/modals/GlyphShowcasePanelModal";
|
||||
import H2PModal from "@/components/modals/H2PModal";
|
||||
import ImportAutomatorDataModal from "@/components/modals/ImportAutomatorDataModal";
|
||||
import ImportFileWarningModal from "@/components/modals/ImportFileWarningModal";
|
||||
import ImportSaveModal from "@/components/modals/ImportSaveModal";
|
||||
import InformationModal from "@/components/modals/InformationModal";
|
||||
import LoadGameModal from "@/components/modals/LoadGameModal";
|
||||
@ -104,6 +103,7 @@ export class Modal {
|
||||
this._uniqueID = nextModalID++;
|
||||
this._props = Object.assign({}, modalConfig || {});
|
||||
if (this._closeEvent) this.applyCloseListeners(this._closeEvent);
|
||||
if (modalConfig?.closeEvent) this.applyCloseListeners(modalConfig.closeEvent);
|
||||
|
||||
const modalQueue = ui.view.modal.queue;
|
||||
// Add this modal to the front of the queue and sort based on priority to ensure priority is maintained.
|
||||
@ -238,7 +238,6 @@ Modal.changelog = new Modal(ChangelogModal, 1);
|
||||
Modal.awayProgress = new Modal(AwayProgressModal);
|
||||
Modal.loadGame = new Modal(LoadGameModal);
|
||||
Modal.import = new Modal(ImportSaveModal);
|
||||
Modal.importWarning = new Modal(ImportFileWarningModal);
|
||||
Modal.importScriptData = new Modal(ImportAutomatorDataModal);
|
||||
Modal.automatorScriptDelete = new Modal(DeleteAutomatorScriptModal);
|
||||
Modal.automatorScriptTemplate = new Modal(AutomatorScriptTemplate);
|
||||
@ -265,7 +264,11 @@ function getSaveInfo(save) {
|
||||
imaginaryMachines: 0,
|
||||
dilatedTime: new Decimal(0),
|
||||
bestLevel: 0,
|
||||
totalSTD: 0,
|
||||
pelleAM: new Decimal(0),
|
||||
remnants: 0,
|
||||
realityShards: new Decimal(0),
|
||||
// This is a slight workaround to hide DT/level once Doomed
|
||||
pelleLore: 0,
|
||||
saveName: "",
|
||||
compositeProgress: 0,
|
||||
};
|
||||
@ -282,7 +285,10 @@ function getSaveInfo(save) {
|
||||
resources.imaginaryMachines = save.reality?.iMCap ?? 0;
|
||||
resources.dilatedTime.copyFrom(new Decimal(save.dilation.dilatedTime));
|
||||
resources.bestLevel = save.records?.bestReality.glyphLevel ?? 0;
|
||||
resources.totalSTD = save?.IAP?.totalSTD ?? 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.compositeProgress = ProgressChecker.getCompositeProgress(save);
|
||||
|
||||
|
@ -15,15 +15,13 @@ export class GameOptions {
|
||||
ui.view.newUI = player.options.newUI;
|
||||
// This is needed because .s-base--dark is on newUI/normal but not on oldUI/normal
|
||||
// So the classes on body need to be updated
|
||||
Themes.find(player.options.theme).set();
|
||||
Themes.find(Theme.currentName()).set();
|
||||
SteamFunctions.UIZoom()
|
||||
GameStorage.save(true);
|
||||
}
|
||||
|
||||
static cloudSave() {
|
||||
// This flag suppresses the modal on autosave, but this function is only called with manual saves
|
||||
Cloud.hasSeenSavingConflict = false;
|
||||
Cloud.saveCheck();
|
||||
Cloud.saveCheck(true);
|
||||
}
|
||||
|
||||
static cloudLoad() {
|
||||
|
@ -52,6 +52,11 @@ export class PlayerProgress {
|
||||
return PlayerProgress.current.isRealityUnlocked;
|
||||
}
|
||||
|
||||
static seenAlteredSpeed() {
|
||||
const ec12 = EternityChallenge(12);
|
||||
return this.realityUnlocked() || ec12.completions > 0 || ec12.isRunning;
|
||||
}
|
||||
|
||||
static challengeCompleted() {
|
||||
return NormalChallenges.all.slice(1).some(c => c.isCompleted);
|
||||
}
|
||||
|
@ -44,7 +44,11 @@ export const Theme = function Theme(name, config) {
|
||||
} else {
|
||||
document.getElementById("background-animations").style.display = "none";
|
||||
}
|
||||
player.options.theme = name;
|
||||
if (player.options.newUI) {
|
||||
player.options.themeModern = name;
|
||||
} else {
|
||||
player.options.themeClassic = name;
|
||||
}
|
||||
ui.view.theme = name;
|
||||
window.getSelection().removeAllRanges();
|
||||
PerkNetwork.forceNetworkRemake();
|
||||
@ -55,8 +59,14 @@ export const Theme = function Theme(name, config) {
|
||||
};
|
||||
};
|
||||
|
||||
Theme.currentName = function() {
|
||||
return player.options.newUI
|
||||
? player.options.themeModern
|
||||
: player.options.themeClassic;
|
||||
};
|
||||
|
||||
Theme.current = function() {
|
||||
return Themes.find(player.options.theme);
|
||||
return Themes.find(Theme.currentName());
|
||||
};
|
||||
|
||||
Theme.set = function(name) {
|
||||
|
@ -32,7 +32,6 @@ export const state = {
|
||||
automator: {
|
||||
fullScreen: false,
|
||||
editorScriptID: "",
|
||||
// TODO: enum
|
||||
lines: []
|
||||
}
|
||||
},
|
||||
|
@ -10,7 +10,7 @@ Autobuyer.annihilation = new class AnnihilationAutobuyerState extends AutobuyerS
|
||||
}
|
||||
|
||||
get isUnlocked() {
|
||||
return Laitela.darkMatterMult > 1 && !Pelle.isDoomed;
|
||||
return SingularityMilestone.annihilationAutobuyer.canBeApplied;
|
||||
}
|
||||
|
||||
get multiplier() {
|
||||
|
@ -11,7 +11,7 @@ new class DarkMatterDimensionAscensionAutobuyerState extends IntervaledAutobuyer
|
||||
}
|
||||
|
||||
get isUnlocked() {
|
||||
return SingularityMilestone.darkDimensionAutobuyers.canBeApplied;
|
||||
return SingularityMilestone.ascensionAutobuyers.canBeApplied;
|
||||
}
|
||||
|
||||
get interval() {
|
||||
@ -24,7 +24,7 @@ new class DarkMatterDimensionAscensionAutobuyerState extends IntervaledAutobuyer
|
||||
|
||||
tick() {
|
||||
super.tick();
|
||||
for (let i = 1; i <= SingularityMilestone.darkDimensionAutobuyers.effectValue; i++) {
|
||||
for (let i = 1; i <= SingularityMilestone.ascensionAutobuyers.effectValue; i++) {
|
||||
DarkMatterDimension(i).ascend();
|
||||
}
|
||||
}
|
||||
|
@ -234,7 +234,7 @@ import { AutomatorLexer } from "./lexer";
|
||||
// things like new Decimal("11,21,31") return 11 instead of something indicating an error.
|
||||
return value.match(/^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?$/u);
|
||||
case AUTOMATOR_VAR_TYPES.STUDIES:
|
||||
return new TimeStudyTree(value).purchasedStudies.length > 0;
|
||||
return TimeStudyTree.isValidImportString(value);
|
||||
case AUTOMATOR_VAR_TYPES.DURATION:
|
||||
return !Number.isNaN(parseInt(1000 * value, 10));
|
||||
default:
|
||||
|
@ -44,11 +44,23 @@ export function bigCrunchResetRequest(disableAnimation = false) {
|
||||
}
|
||||
}
|
||||
|
||||
export function bigCrunchReset() {
|
||||
if (!Player.canCrunch) return;
|
||||
export function bigCrunchReset(
|
||||
forced = false,
|
||||
enteringAntimatterChallenge = Player.isInAntimatterChallenge && player.options.retryChallenge
|
||||
) {
|
||||
if (!forced && !Player.canCrunch) return;
|
||||
|
||||
EventHub.dispatch(GAME_EVENT.BIG_CRUNCH_BEFORE);
|
||||
if (Player.canCrunch) {
|
||||
EventHub.dispatch(GAME_EVENT.BIG_CRUNCH_BEFORE);
|
||||
bigCrunchGiveRewards();
|
||||
if (Pelle.isDoomed) PelleStrikes.infinity.trigger();
|
||||
}
|
||||
|
||||
bigCrunchResetValues(enteringAntimatterChallenge);
|
||||
EventHub.dispatch(GAME_EVENT.BIG_CRUNCH_AFTER);
|
||||
}
|
||||
|
||||
function bigCrunchGiveRewards() {
|
||||
bigCrunchUpdateStatistics();
|
||||
|
||||
const infinityPoints = gainedInfinityPoints();
|
||||
@ -56,12 +68,7 @@ export function bigCrunchReset() {
|
||||
Currency.infinities.add(gainedInfinities().round());
|
||||
|
||||
bigCrunchTabChange(!PlayerProgress.infinityUnlocked());
|
||||
bigCrunchResetValues();
|
||||
bigCrunchCheckUnlocks();
|
||||
|
||||
if (Pelle.isDoomed) PelleStrikes.infinity.trigger();
|
||||
|
||||
EventHub.dispatch(GAME_EVENT.BIG_CRUNCH_AFTER);
|
||||
}
|
||||
|
||||
function bigCrunchUpdateStatistics() {
|
||||
@ -108,13 +115,13 @@ function bigCrunchTabChange(firstInfinity) {
|
||||
}
|
||||
}
|
||||
|
||||
export function bigCrunchResetValues() {
|
||||
export function bigCrunchResetValues(enteringAntimatterChallenge) {
|
||||
const currentReplicanti = Replicanti.amount;
|
||||
const currentReplicantiGalaxies = player.replicanti.galaxies;
|
||||
// For unknown reasons, everything but keeping of RGs (including resetting of RGs)
|
||||
// is done in the function called below. For now, we're just trying to keep
|
||||
// code structure similar to what it was before to avoid new bugs.
|
||||
secondSoftReset(true);
|
||||
secondSoftReset(enteringAntimatterChallenge);
|
||||
|
||||
let remainingGalaxies = 0;
|
||||
if (Achievement(95).isUnlocked) {
|
||||
@ -142,12 +149,12 @@ function bigCrunchCheckUnlocks() {
|
||||
}
|
||||
}
|
||||
|
||||
export function secondSoftReset(forcedNDReset = false) {
|
||||
export function secondSoftReset(enteringAntimatterChallenge) {
|
||||
player.dimensionBoosts = 0;
|
||||
player.galaxies = 0;
|
||||
player.records.thisInfinity.maxAM = DC.D0;
|
||||
Currency.antimatter.reset();
|
||||
softReset(0, forcedNDReset);
|
||||
softReset(0, true, true, enteringAntimatterChallenge);
|
||||
InfinityDimensions.resetAmount();
|
||||
if (player.replicanti.unl) Replicanti.amount = DC.D1;
|
||||
player.replicanti.galaxies = 0;
|
||||
|
@ -220,10 +220,13 @@ class BlackHoleState {
|
||||
return this.isCharged && (this.id === 1 || BlackHole(this.id - 1).isActive) && !Pelle.isDisabled("blackhole");
|
||||
}
|
||||
|
||||
// Proportion of active time, scaled 0 to 1
|
||||
get dutyCycle() {
|
||||
return this.duration / (this.rawInterval + this.duration);
|
||||
}
|
||||
|
||||
get isPermanent() {
|
||||
// If the black hole is active 99.99% of the time, the duration is exactly
|
||||
// 9999 times longer than the interval.
|
||||
return this.duration / this.rawInterval >= 9999;
|
||||
return this.dutyCycle >= 0.9999;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -351,6 +354,7 @@ export const BlackHoles = {
|
||||
Currency.realityMachines.purchase(100);
|
||||
SpeedrunMilestones(17).tryComplete();
|
||||
Achievement(144).unlock();
|
||||
player.records.timePlayedAtBHUnlock = player.records.totalTimePlayed;
|
||||
EventHub.dispatch(GAME_EVENT.BLACK_HOLE_UNLOCKED);
|
||||
},
|
||||
|
||||
@ -364,7 +368,7 @@ export const BlackHoles = {
|
||||
// whether the player's using negative BH (i.e. BH inversion); if going paused -> unpaused,
|
||||
// use "unpaused".
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
const pauseType = player.blackHolePause ? (BlackHoles.areNegative ? "inverted" : "paused") : 'unpaused';
|
||||
const pauseType = player.blackHolePause ? (BlackHoles.areNegative ? "inverted" : "paused") : "unpaused";
|
||||
const automaticString = automatic ? "automatically " : "";
|
||||
GameUI.notify.blackHole(`${blackHoleString} ${automaticString}${pauseType}`);
|
||||
},
|
||||
@ -380,7 +384,7 @@ export const BlackHoles = {
|
||||
},
|
||||
|
||||
get areNegative() {
|
||||
return this.arePaused && player.blackHoleNegative < 1;
|
||||
return this.arePaused && !Laitela.isRunning && player.blackHoleNegative < 1;
|
||||
},
|
||||
|
||||
get arePermanent() {
|
||||
@ -560,18 +564,18 @@ export const BlackHoles = {
|
||||
timeToNextPause(bhNum, steps = 100) {
|
||||
if (bhNum === 1) {
|
||||
// This is a simple case that we can do mathematically.
|
||||
const bh = BlackHole(1);
|
||||
// If no blackhole gaps are as long as the warmup time, we never pause.
|
||||
if (bh.interval <= BlackHoles.ACCELERATION_TIME) {
|
||||
return null;
|
||||
}
|
||||
// Find the time until next activation.
|
||||
const t = (bh.isCharged ? bh.duration : 0) + bh.interval - bh.phase;
|
||||
// If the time until next activation is less than the acceleration time,
|
||||
// we have to wait until the activation after that;
|
||||
// otherwise, we can just use the next activation.
|
||||
return (t < BlackHoles.ACCELERATION_TIME)
|
||||
? t + bh.duration + bh.interval - BlackHoles.ACCELERATION_TIME : t - BlackHoles.ACCELERATION_TIME;
|
||||
const bh = BlackHole(1);
|
||||
// If no blackhole gaps are as long as the warmup time, we never pause.
|
||||
if (bh.interval <= BlackHoles.ACCELERATION_TIME) {
|
||||
return null;
|
||||
}
|
||||
// Find the time until next activation.
|
||||
const t = (bh.isCharged ? bh.duration : 0) + bh.interval - bh.phase;
|
||||
// If the time until next activation is less than the acceleration time,
|
||||
// we have to wait until the activation after that;
|
||||
// otherwise, we can just use the next activation.
|
||||
return (t < BlackHoles.ACCELERATION_TIME)
|
||||
? t + bh.duration + bh.interval - BlackHoles.ACCELERATION_TIME : t - BlackHoles.ACCELERATION_TIME;
|
||||
}
|
||||
// Look at the next 100 black hole transitions.
|
||||
// This is called every tick if BH pause setting is set to BH2, so we try to optimize it.
|
||||
@ -609,11 +613,11 @@ export const BlackHoles = {
|
||||
const phaseBounds = phaseBoundList[current];
|
||||
// Compute time until some phase reaches its bound.
|
||||
const minTime = current > 0 ? Math.min(phaseBounds[0] - phases[0], phaseBounds[1] - phases[1])
|
||||
: phaseBounds[0] - phases[0];
|
||||
: phaseBounds[0] - phases[0];
|
||||
if (current === 2) {
|
||||
// Check if there was enough time before this activation to pause.
|
||||
if (inactiveTime >= BlackHoles.ACCELERATION_TIME) {
|
||||
return totalTime - BlackHoles.ACCELERATION_TIME;
|
||||
return totalTime - BlackHoles.ACCELERATION_TIME;
|
||||
}
|
||||
// Not enough time, reset inactive time to 0.
|
||||
inactiveTime = 0;
|
||||
|
@ -66,7 +66,7 @@ GameDatabase.celestials.descriptions = [
|
||||
{
|
||||
name: "Ra",
|
||||
effects() {
|
||||
return `You only have ${formatInt(4)} Dimension Boosts and can't gain any more.
|
||||
return `You only have ${formatInt(4)} Dimension Boosts and can not gain any more.
|
||||
The Tickspeed purchase multiplier is fixed at ${formatX(1.1245, 0, 3)}.`;
|
||||
},
|
||||
},
|
||||
@ -98,7 +98,7 @@ GameDatabase.celestials.descriptions = [
|
||||
|
||||
return `Infinity Point and Eternity Point gain are Dilated.
|
||||
Game speed is reduced to ${formatInt(1)} and gradually comes back over ${formatInt(10)} minutes.
|
||||
Black Hole storing, discharging, and pulsing are disabled.
|
||||
Black Hole storing, discharging, pulsing, and inversion are all disabled.
|
||||
${disabledText}`;
|
||||
},
|
||||
description() {
|
||||
|
@ -83,15 +83,19 @@ export const Effarig = {
|
||||
}
|
||||
return 3 * (1 - c / (c + Math.sqrt(power.pLog10())));
|
||||
},
|
||||
get tickDilation() {
|
||||
return 0.7 + 0.1 * this.nerfFactor(Currency.timeShards.value);
|
||||
},
|
||||
get multDilation() {
|
||||
return 0.25 + 0.25 * this.nerfFactor(Currency.infinityPower.value);
|
||||
},
|
||||
get tickspeed() {
|
||||
const base = 3 + Tickspeed.baseValue.reciprocal().log10();
|
||||
const pow = 0.7 + 0.1 * this.nerfFactor(Currency.timeShards.value);
|
||||
return Decimal.pow10(Math.pow(base, pow)).reciprocal();
|
||||
return Decimal.pow10(Math.pow(base, this.tickDilation)).reciprocal();
|
||||
},
|
||||
multiplier(mult) {
|
||||
const base = new Decimal(mult).pLog10();
|
||||
const pow = 0.25 + 0.25 * this.nerfFactor(Currency.infinityPower.value);
|
||||
return Decimal.pow10(Math.pow(base, pow));
|
||||
return Decimal.pow10(Math.pow(base, this.multDilation));
|
||||
},
|
||||
get bonusRG() {
|
||||
// Will return 0 if Effarig Infinity is uncompleted
|
||||
|
@ -183,10 +183,14 @@ export const Enslaved = {
|
||||
return Math.pow(10, Math.pow(Math.log10(stored / 1e3), 0.55)) * 1e3;
|
||||
},
|
||||
feelEternity() {
|
||||
if (!this.feltEternity) {
|
||||
if (this.feltEternity) {
|
||||
Modal.message.show(`You have already exposed this crack in the Reality. Time in this Eternity is being multiplied
|
||||
by your Eternity count, up to a maximum of ${formatX(1e66)}.`,
|
||||
{ closeEvent: GAME_EVENT.REALITY_RESET_AFTER }, 1);
|
||||
} else {
|
||||
EnslavedProgress.feelEternity.giveProgress();
|
||||
this.feltEternity = true;
|
||||
Modal.message.show(`Time in this Eternity will be multiplied by number of Eternities,
|
||||
Modal.message.show(`Time in this Eternity will be multiplied by your Eternity count,
|
||||
up to a maximum of ${formatX(1e66)}.`, { closeEvent: GAME_EVENT.REALITY_RESET_AFTER }, 1);
|
||||
}
|
||||
},
|
||||
|
@ -129,9 +129,10 @@ export const SingularityMilestones = {
|
||||
case SINGULARITY_MILESTONE_SORT.CURRENT_COMPLETIONS:
|
||||
// Also counts partial completion on the current step
|
||||
sortFn = m => {
|
||||
const currComp = Math.clampMax(Math.log(Currency.singularities.value / m.previousGoal) /
|
||||
Math.log(m.nextGoal / m.previousGoal), 1);
|
||||
return (m.completions + currComp) / 20;
|
||||
// For never-completed repeatable milestones, this is zero and will cause NaN bugs if we don't set it to 1
|
||||
const prev = Math.clampMin(m.previousGoal, 1);
|
||||
const part = Math.clamp(Math.log(Currency.singularities.value / prev) / Math.log(m.nextGoal / prev), 0, 1);
|
||||
return (m.completions + part) / 20;
|
||||
};
|
||||
break;
|
||||
case SINGULARITY_MILESTONE_SORT.PERCENT_COMPLETIONS:
|
||||
|
@ -138,7 +138,7 @@ export const Pelle = {
|
||||
|
||||
get uselessPerks() {
|
||||
return [10, 12, 13, 14, 15, 16, 17, 30, 40, 41, 42, 43, 44, 45, 46, 51, 53,
|
||||
60, 61, 62, 80, 81, 82, 83, 100, 105, 106, 201, 202, 203, 204];
|
||||
60, 61, 62, 80, 81, 82, 83, 100, 104, 105, 106, 201, 202, 203, 204];
|
||||
},
|
||||
|
||||
get specialGlyphEffect() {
|
||||
|
@ -248,13 +248,6 @@ export const Ra = {
|
||||
data.peakGamespeed = 1;
|
||||
for (const pet of Ra.pets.all) pet.reset();
|
||||
},
|
||||
// Scans through all glyphs and fills base resources to the maximum allowed by the cap
|
||||
// TODO update/delete this function when we get back to alchemy, it's outdated since it's not linear any more
|
||||
fillAlchemyResources() {
|
||||
for (const resource of AlchemyResources.base) {
|
||||
resource.amount = Math.min(this.alchemyResourceCap, player.records.bestReality.glyphLevel);
|
||||
}
|
||||
},
|
||||
memoryTick(realDiff, generateChunks) {
|
||||
if (!this.isUnlocked) return;
|
||||
for (const pet of Ra.pets.all) pet.tick(realDiff, generateChunks);
|
||||
@ -284,12 +277,19 @@ export const Ra = {
|
||||
const post15Scaling = Math.pow(1.5, Math.max(0, level - 15));
|
||||
return Math.floor(Math.pow(adjustedLevel, 5.52) * post15Scaling * 1e6);
|
||||
},
|
||||
// Calculates the cumulative exp needed to REACH a level starting from nothing.
|
||||
// TODO mathematically optimize this once Ra exp curves and balancing are finalized
|
||||
totalExpForLevel(maxLevel) {
|
||||
let runningTotal = 0;
|
||||
for (let lv = 1; lv < maxLevel; lv++) runningTotal += this.requiredMemoriesForLevel(lv);
|
||||
return runningTotal;
|
||||
// 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 b = Ra.productionPerMemoryChunk * pet.memoryUpgradeCurrentMult * pet.memoryChunks;
|
||||
const c = -expToGain;
|
||||
const estimate = a === 0
|
||||
? -c / b
|
||||
: (Math.sqrt(Math.pow(b, 2) - 4 * a * c) - b) / (2 * a);
|
||||
if (Number.isFinite(estimate)) {
|
||||
return `in ${TimeSpan.fromSeconds(estimate).toStringShort()}`;
|
||||
}
|
||||
return "";
|
||||
},
|
||||
get totalPetLevel() {
|
||||
return this.pets.all.map(pet => (pet.isUnlocked ? pet.level : 0)).sum();
|
||||
|
@ -463,3 +463,5 @@ window.PROGRESS_STAGE = {
|
||||
LAITELA: 16,
|
||||
PELLE: 17,
|
||||
};
|
||||
|
||||
window.STD_BACKEND_URL = "https://antimatterdimensionspayments.ew.r.appspot.com";
|
||||
|
@ -396,10 +396,11 @@ Currency.realities = new class extends NumberCurrency {
|
||||
Currency.realityMachines = new class extends DecimalCurrency {
|
||||
get value() { return player.reality.realityMachines; }
|
||||
set value(value) {
|
||||
const cappedValue = Decimal.min(value, MachineHandler.hardcapRM);
|
||||
player.reality.realityMachines = cappedValue;
|
||||
if (player.records.bestReality.RM.lt(cappedValue)) {
|
||||
player.records.bestReality.RM = cappedValue;
|
||||
const newValue = Decimal.min(value, MachineHandler.hardcapRM);
|
||||
const addedThisReality = newValue.minus(player.reality.realityMachines);
|
||||
player.reality.realityMachines = newValue;
|
||||
if (player.records.bestReality.RM.lt(addedThisReality)) {
|
||||
player.records.bestReality.RM = addedThisReality;
|
||||
player.records.bestReality.RMSet = Glyphs.copyForRecords(Glyphs.active.filter(g => g !== null));
|
||||
}
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ export function getDilationGainPerSecond() {
|
||||
return dtRate;
|
||||
}
|
||||
|
||||
function tachyonGainMultiplier() {
|
||||
export function tachyonGainMultiplier() {
|
||||
if (Pelle.isDisabled("tpMults")) return new Decimal(1);
|
||||
const pow = Enslaved.isRunning ? Enslaved.tachyonNerf : 1;
|
||||
return DC.D1.timesEffectsOf(
|
||||
@ -151,7 +151,7 @@ function tachyonGainMultiplier() {
|
||||
}
|
||||
|
||||
export function rewardTP() {
|
||||
Currency.tachyonParticles.bumpTo(getTP(Currency.antimatter.value, true));
|
||||
Currency.tachyonParticles.bumpTo(getTP(player.records.thisEternity.maxAM, true));
|
||||
player.dilation.lastEP = Currency.eternityPoints.value;
|
||||
}
|
||||
|
||||
@ -182,17 +182,32 @@ export function getTachyonGain(requireEternity) {
|
||||
|
||||
// Returns the minimum antimatter needed in order to gain more TP; used only for display purposes
|
||||
export function getTachyonReq() {
|
||||
let effectiveTP = Currency.tachyonParticles.value;
|
||||
let effectiveTP = Currency.tachyonParticles.value.dividedBy(tachyonGainMultiplier());
|
||||
if (Enslaved.isRunning) effectiveTP = effectiveTP.pow(1 / Enslaved.tachyonNerf);
|
||||
return Decimal.pow10(
|
||||
effectiveTP
|
||||
.times(Math.pow(400, 1.5))
|
||||
.dividedBy(tachyonGainMultiplier())
|
||||
.pow(2 / 3)
|
||||
.toNumber()
|
||||
);
|
||||
}
|
||||
|
||||
export function getDilationTimeEstimate(goal) {
|
||||
const currentDTGain = getDilationGainPerSecond();
|
||||
const rawDTGain = currentDTGain.times(getGameSpeedupForDisplay());
|
||||
const currentDT = Currency.dilatedTime.value;
|
||||
if (currentDTGain.eq(0)) return null;
|
||||
if (PelleRifts.paradox.isActive) {
|
||||
const drain = Pelle.riftDrainPercent;
|
||||
const goalNetRate = rawDTGain.minus(Decimal.multiply(goal, drain));
|
||||
const currNetRate = rawDTGain.minus(currentDT.times(drain));
|
||||
if (goalNetRate.lt(0)) return "Never affordable due to Rift drain";
|
||||
return TimeSpan.fromSeconds(currNetRate.div(goalNetRate).ln() / drain).toTimeEstimate();
|
||||
}
|
||||
return TimeSpan.fromSeconds(Decimal.sub(goal, currentDT)
|
||||
.div(rawDTGain).toNumber()).toTimeEstimate();
|
||||
}
|
||||
|
||||
export function dilatedValueOf(value) {
|
||||
const log10 = value.log10();
|
||||
const dilationPenalty = 0.75 * Effects.product(DilationUpgrade.dilationPenalty);
|
||||
|
@ -173,7 +173,8 @@ export class DimBoost {
|
||||
}
|
||||
}
|
||||
|
||||
export function softReset(tempBulk, forcedNDReset = false, forcedAMReset = false) {
|
||||
// eslint-disable-next-line max-params
|
||||
export function softReset(tempBulk, forcedADReset = false, forcedAMReset = false, enteringAntimatterChallenge = false) {
|
||||
if (Currency.antimatter.gt(Player.infinityLimit)) return;
|
||||
const bulk = Math.min(tempBulk, DimBoost.maxBoosts - player.dimensionBoosts);
|
||||
EventHub.dispatch(GAME_EVENT.DIMBOOST_BEFORE, bulk);
|
||||
@ -182,12 +183,12 @@ export function softReset(tempBulk, forcedNDReset = false, forcedAMReset = false
|
||||
const canKeepDimensions = Pelle.isDoomed
|
||||
? PelleUpgrade.dimBoostResetsNothing.canBeApplied
|
||||
: Perk.antimatterNoReset.canBeApplied;
|
||||
if (forcedNDReset || !canKeepDimensions) {
|
||||
if (forcedADReset || !canKeepDimensions) {
|
||||
AntimatterDimensions.reset();
|
||||
player.sacrificed = DC.D0;
|
||||
resetTickspeed();
|
||||
}
|
||||
skipResetsIfPossible();
|
||||
skipResetsIfPossible(enteringAntimatterChallenge);
|
||||
const canKeepAntimatter = Pelle.isDoomed
|
||||
? PelleUpgrade.dimBoostResetsNothing.canBeApplied
|
||||
: (Achievement(111).isUnlocked || Perk.antimatterNoReset.canBeApplied);
|
||||
@ -199,8 +200,8 @@ export function softReset(tempBulk, forcedNDReset = false, forcedAMReset = false
|
||||
EventHub.dispatch(GAME_EVENT.DIMBOOST_AFTER, bulk);
|
||||
}
|
||||
|
||||
export function skipResetsIfPossible() {
|
||||
if (Player.isInAntimatterChallenge) return;
|
||||
export function skipResetsIfPossible(enteringAntimatterChallenge) {
|
||||
if (enteringAntimatterChallenge || Player.isInAntimatterChallenge) return;
|
||||
if (InfinityUpgrade.skipResetGalaxy.isBought && player.dimensionBoosts < 4) {
|
||||
player.dimensionBoosts = 4;
|
||||
if (player.galaxies === 0) player.galaxies = 1;
|
||||
|
@ -439,6 +439,19 @@ class AntimatterDimensionState extends DimensionState {
|
||||
return toGain.times(10).dividedBy(this.amount.max(1)).times(getGameSpeedupForDisplay());
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get isProducing() {
|
||||
const tier = this.tier;
|
||||
if ((EternityChallenge(3).isRunning && tier > 4) ||
|
||||
(NormalChallenge(12).isRunning && tier > 6) ||
|
||||
(Laitela.isRunning && tier > Laitela.maxAllowedDimension)) {
|
||||
return false;
|
||||
}
|
||||
return this.totalAmount.gt(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Decimal}
|
||||
*/
|
||||
|
@ -180,6 +180,16 @@ class InfinityDimensionState extends DimensionState {
|
||||
return mult;
|
||||
}
|
||||
|
||||
get isProducing() {
|
||||
const tier = this.tier;
|
||||
if (EternityChallenge(2).isRunning ||
|
||||
EternityChallenge(10).isRunning ||
|
||||
(Laitela.isRunning && tier > Laitela.maxAllowedDimension)) {
|
||||
return false;
|
||||
}
|
||||
return this.amount.gt(0);
|
||||
}
|
||||
|
||||
get baseCost() {
|
||||
return this._baseCost;
|
||||
}
|
||||
|
@ -233,6 +233,16 @@ class TimeDimensionState extends DimensionState {
|
||||
return toGain.times(10).dividedBy(current).times(getGameSpeedupForDisplay());
|
||||
}
|
||||
|
||||
get isProducing() {
|
||||
const tier = this.tier;
|
||||
if (EternityChallenge(1).isRunning ||
|
||||
EternityChallenge(10).isRunning ||
|
||||
(Laitela.isRunning && tier > Laitela.maxAllowedDimension)) {
|
||||
return false;
|
||||
}
|
||||
return this.amount.gt(0);
|
||||
}
|
||||
|
||||
get baseCost() {
|
||||
return this._baseCost;
|
||||
}
|
||||
|
@ -208,7 +208,9 @@ function askEternityConfirmation() {
|
||||
export function gainedEternities() {
|
||||
return Pelle.isDisabled("eternityMults")
|
||||
? new Decimal(1)
|
||||
: new Decimal(getAdjustedGlyphEffect("timeetermult")).timesEffectsOf(RealityUpgrade(3), Achievement(113));
|
||||
: new Decimal(getAdjustedGlyphEffect("timeetermult"))
|
||||
.timesEffectsOf(RealityUpgrade(3), Achievement(113))
|
||||
.pow(AlchemyResource.eternity.effectValue);
|
||||
}
|
||||
|
||||
export class EternityMilestoneState {
|
||||
|
@ -90,6 +90,7 @@ export class Galaxy {
|
||||
if (this.canBeBought) return null;
|
||||
if (EternityChallenge(6).isRunning) return "Locked (Eternity Challenge 6)";
|
||||
if (InfinityChallenge(7).isRunning) return "Locked (Infinity Challenge 7)";
|
||||
if (InfinityChallenge(1).isRunning) return "Locked (Infinity Challenge 1)";
|
||||
if (NormalChallenge(8).isRunning) return "Locked (8th Antimatter Dimension Autobuyer Challenge)";
|
||||
return null;
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ export * from "./black_hole";
|
||||
export * from "./machines";
|
||||
export * from "./devtools";
|
||||
export * from "./news-ticker";
|
||||
export * from "./kong";
|
||||
export * from "./shop";
|
||||
export * from "./ui/tabs";
|
||||
export * from "./ui/tab-notifications";
|
||||
export * from "./speedrun";
|
||||
|
@ -167,9 +167,10 @@ function getGlyphLevelSources() {
|
||||
// Glyph levels are the product of 3 or 4 sources (eternities are enabled via upgrade).
|
||||
// Once Effarig is unlocked, these contributions can be adjusted; the math is described in detail
|
||||
// in getGlyphLevelInputs. These *Base values are the nominal inputs, as they would be multiplied without Effarig
|
||||
const eternityPoints = Player.canEternity
|
||||
let eternityPoints = Player.canEternity
|
||||
? Currency.eternityPoints.value.plus(gainedEternityPoints())
|
||||
: Currency.eternityPoints.value;
|
||||
eternityPoints = Decimal.max(player.records.thisReality.maxEP, eternityPoints);
|
||||
const epCoeff = 0.016;
|
||||
const epBase = Math.pow(Math.max(1, eternityPoints.pLog10()), 0.5) * epCoeff;
|
||||
const replPow = 0.4 + getAdjustedGlyphEffect("replicationglyphlevel");
|
||||
|
@ -232,13 +232,10 @@ export const Glyphs = {
|
||||
return this.active[activeIndex];
|
||||
},
|
||||
equip(glyph, targetSlot) {
|
||||
const pelleMaxGlyphs = 1;
|
||||
const glyphsEquipped = Glyphs.active.filter(Boolean).length;
|
||||
if (
|
||||
Pelle.isDoomed &&
|
||||
(
|
||||
Pelle.isDisabled("glyphs") ||
|
||||
glyphsEquipped >= pelleMaxGlyphs ||
|
||||
["effarig", "reality", "cursed"].includes(glyph.type)
|
||||
)
|
||||
) return;
|
||||
@ -619,7 +616,9 @@ export const Glyphs = {
|
||||
Currency.tachyonParticles.value = new Decimal(undoData.tp);
|
||||
Currency.dilatedTime.value = new Decimal(undoData.dt);
|
||||
}
|
||||
if (AutomatorBackend.state.forceRestart) AutomatorBackend.restart();
|
||||
if (Player.automatorUnlocked && AutomatorBackend.state.forceRestart) {
|
||||
AutomatorBackend.start(player.reality.automator.state.editorScript);
|
||||
}
|
||||
},
|
||||
copyForRecords(glyphList) {
|
||||
// Sorting by effect ensures consistent ordering by type, based on how the effect bitmasks are structured
|
||||
|
@ -49,11 +49,12 @@ class ImaginaryUpgradeState extends BitPurchasableMechanicState {
|
||||
EventHub.dispatch(GAME_EVENT.REALITY_UPGRADE_BOUGHT);
|
||||
if (this.id >= 15 && this.id <= 18) {
|
||||
DarkMatterDimension(this.id - 14).amount = DC.D1;
|
||||
Tab.celestials.laitela.show();
|
||||
if (this.id === 17) Laitela.quotes.thirdDMD.show();
|
||||
}
|
||||
if (this.id === 19) {
|
||||
Tab.celestials.laitela.show();
|
||||
if (this.id >= 15 && this.id <= 19) {
|
||||
// Need to clear before retriggering, or else it won't actually show up on subsequent upgrades
|
||||
TabNotification.laitelaUnlock.clearTrigger();
|
||||
TabNotification.laitelaUnlock.tryTrigger();
|
||||
}
|
||||
if (this.id === 21) {
|
||||
Laitela.quotes.finalRowIM.show();
|
||||
@ -62,7 +63,7 @@ class ImaginaryUpgradeState extends BitPurchasableMechanicState {
|
||||
BASIC_GLYPH_TYPES.forEach(x => player.reality.glyphs.sac[x] = ImaginaryUpgrade(22).effectValue);
|
||||
}
|
||||
if (this.id === 25) {
|
||||
Tab.celestials.pelle.show();
|
||||
TabNotification.pelleUnlock.tryTrigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,9 +49,10 @@ class InfinityChallengeState extends GameMechanicState {
|
||||
|
||||
start() {
|
||||
if (!this.isUnlocked || this.isRunning) return;
|
||||
// Forces big crunch reset but ensures IP gain, if any.
|
||||
bigCrunchReset(true, true);
|
||||
player.challenge.normal.current = 0;
|
||||
player.challenge.infinity.current = this.id;
|
||||
bigCrunchResetValues();
|
||||
if (!Enslaved.isRunning) Tab.dimensions.antimatter.show();
|
||||
player.break = true;
|
||||
if (EternityChallenge.isRunning) Achievement(115).unlock();
|
||||
@ -90,14 +91,13 @@ class InfinityChallengeState extends GameMechanicState {
|
||||
if (bestTimes[this.id - 1] <= player.records.thisInfinity.time) {
|
||||
return;
|
||||
}
|
||||
// TODO: remove splice once player.challenge.infinity.bestTimes is not reactive
|
||||
bestTimes.splice(this.id - 1, 1, player.records.thisInfinity.time);
|
||||
player.challenge.infinity.bestTimes[this.id - 1] = player.records.thisInfinity.time;
|
||||
GameCache.infinityChallengeTimeSum.invalidate();
|
||||
}
|
||||
|
||||
exit() {
|
||||
player.challenge.infinity.current = 0;
|
||||
bigCrunchResetValues();
|
||||
bigCrunchReset(true, false);
|
||||
if (!Enslaved.isRunning) Tab.dimensions.antimatter.show();
|
||||
}
|
||||
}
|
||||
|
@ -1,217 +0,0 @@
|
||||
import { RebuyableMechanicState } from "./game-mechanics/index";
|
||||
|
||||
export const kong = {};
|
||||
|
||||
kong.enabled = false;
|
||||
|
||||
kong.init = function() {
|
||||
if (document.referrer.indexOf("kongregate") === -1)
|
||||
return;
|
||||
kong.enabled = true;
|
||||
try {
|
||||
kongregateAPI.loadAPI(() => {
|
||||
window.kongregate = kongregateAPI.getAPI();
|
||||
kong.updatePurchases();
|
||||
});
|
||||
// eslint-disable-next-line no-console
|
||||
} catch (err) { console.log("Couldn't load Kongregate API"); }
|
||||
};
|
||||
|
||||
class ShopPurchaseState extends RebuyableMechanicState {
|
||||
|
||||
get currency() {
|
||||
return player.IAP.totalSTD - player.IAP.spentSTD;
|
||||
}
|
||||
|
||||
get isAffordable() {
|
||||
return this.currency >= this.cost;
|
||||
}
|
||||
|
||||
get description() {
|
||||
return this.config.description;
|
||||
}
|
||||
|
||||
get cost() {
|
||||
return this.config.cost;
|
||||
}
|
||||
|
||||
get purchases() {
|
||||
return player.IAP[this.config.key];
|
||||
}
|
||||
|
||||
set purchases(value) {
|
||||
player.IAP[this.config.key] = value;
|
||||
}
|
||||
|
||||
get shouldDisplayMult() {
|
||||
return Boolean(this.config.multiplier);
|
||||
}
|
||||
|
||||
get currentMult() {
|
||||
if (!this.shouldDisplayMult) return "";
|
||||
return this.config.multiplier(player.IAP.disabled ? 0 : this.purchases);
|
||||
}
|
||||
|
||||
get nextMult() {
|
||||
if (!this.shouldDisplayMult) return "";
|
||||
return this.config.multiplier(player.IAP.disabled ? 0 : this.purchases + 1);
|
||||
}
|
||||
|
||||
// We want to still display the correct value in the button, so we need separate getters for it
|
||||
get currentMultForDisplay() {
|
||||
if (!this.shouldDisplayMult) return "";
|
||||
return this.config.multiplier(this.purchases);
|
||||
}
|
||||
|
||||
get nextMultForDisplay() {
|
||||
if (!this.shouldDisplayMult) return "";
|
||||
return this.config.multiplier(this.purchases + 1);
|
||||
}
|
||||
|
||||
formatEffect(effect) {
|
||||
return this.config.formatEffect?.(effect) || formatX(effect, 2, 0);
|
||||
}
|
||||
|
||||
purchase() {
|
||||
if (!this.canBeBought) return false;
|
||||
if (GameEnd.creditsEverClosed) return false;
|
||||
if (this.config.singleUse && ui.$viewModel.modal.progressBar) return false;
|
||||
player.IAP.spentSTD += this.cost;
|
||||
if (this.config.singleUse) {
|
||||
this.config.onPurchase();
|
||||
} else {
|
||||
this.purchases++;
|
||||
}
|
||||
GameUI.update();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export const ShopPurchase = mapGameDataToObject(
|
||||
GameDatabase.shopPurchases,
|
||||
config => new ShopPurchaseState(config)
|
||||
);
|
||||
|
||||
ShopPurchase.respecAll = function() {
|
||||
for (const purchase of ShopPurchase.all) {
|
||||
if (purchase.config.singleUse) continue;
|
||||
player.IAP.spentSTD -= purchase.purchases * purchase.cost;
|
||||
purchase.purchases = 0;
|
||||
}
|
||||
};
|
||||
|
||||
ShopPurchase.respecRequest = function() {
|
||||
if (player.options.confirmations.respecIAP) {
|
||||
Modal.respecIAP.show();
|
||||
} else {
|
||||
ShopPurchase.respecAll();
|
||||
}
|
||||
};
|
||||
|
||||
kong.purchaseTimeSkip = function() {
|
||||
simulateTime(3600 * 6, true);
|
||||
};
|
||||
|
||||
kong.purchaseLongerTimeSkip = function() {
|
||||
simulateTime(3600 * 24, true);
|
||||
};
|
||||
|
||||
kong.updatePurchases = function() {
|
||||
if (!kong.enabled) return;
|
||||
try {
|
||||
kongregate.mtx.requestUserItemList("", items);
|
||||
// eslint-disable-next-line no-console
|
||||
} catch (e) { console.error(e); }
|
||||
|
||||
function items(result) {
|
||||
let totalSTD = player.IAP.totalSTD;
|
||||
for (let i = 0; i < result.data.length; i++) {
|
||||
const item = result.data[i];
|
||||
switch (item.identifier) {
|
||||
case "doublemult":
|
||||
totalSTD += 30;
|
||||
break;
|
||||
|
||||
case "doubleip":
|
||||
totalSTD += 40;
|
||||
break;
|
||||
|
||||
case "tripleep":
|
||||
totalSTD += 50;
|
||||
break;
|
||||
|
||||
case "alldimboost":
|
||||
totalSTD += 60;
|
||||
break;
|
||||
|
||||
case "20worthofstd":
|
||||
totalSTD += 20;
|
||||
break;
|
||||
|
||||
case "50worthofstd":
|
||||
totalSTD += 60;
|
||||
break;
|
||||
|
||||
case "100worthofstd":
|
||||
totalSTD += 140;
|
||||
break;
|
||||
|
||||
case "200worthofstd":
|
||||
totalSTD += 300;
|
||||
break;
|
||||
|
||||
case "500worthofstd":
|
||||
totalSTD += 1000;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
if (player.IAP.totalSTD !== totalSTD) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`STD amounts don't match! ${player.IAP.totalSTD} in save, ${totalSTD} in kong`);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
kong.migratePurchases = function() {
|
||||
if (!kong.enabled) return;
|
||||
try {
|
||||
kongregate.mtx.requestUserItemList("", items);
|
||||
// eslint-disable-next-line no-console
|
||||
} catch (e) { console.log(e); }
|
||||
|
||||
function items(result) {
|
||||
let ipPurchases = 0;
|
||||
let dimPurchases = 0;
|
||||
let epPurchases = 0;
|
||||
let alldimPurchases = 0;
|
||||
for (const item of result.data) {
|
||||
if (item.identifier === "doublemult") {
|
||||
player.IAP.totalSTD += 30;
|
||||
player.IAP.spentSTD += 30;
|
||||
dimPurchases++;
|
||||
}
|
||||
if (item.identifier === "doubleip") {
|
||||
player.IAP.totalSTD += 40;
|
||||
player.IAP.spentSTD += 40;
|
||||
ipPurchases++;
|
||||
}
|
||||
if (item.identifier === "tripleep") {
|
||||
player.IAP.totalSTD += 50;
|
||||
player.IAP.spentSTD += 50;
|
||||
epPurchases++;
|
||||
}
|
||||
if (item.identifier === "alldimboost") {
|
||||
player.IAP.totalSTD += 60;
|
||||
player.IAP.spentSTD += 60;
|
||||
alldimPurchases++;
|
||||
}
|
||||
|
||||
}
|
||||
player.IAP.dimPurchases = dimPurchases;
|
||||
player.IAP.allDimPurchases = alldimPurchases;
|
||||
player.IAP.IPPurchases = ipPurchases;
|
||||
player.IAP.EPPurchases = epPurchases;
|
||||
}
|
||||
};
|
@ -4,6 +4,9 @@ export const NG = {
|
||||
startNewGame() {
|
||||
GameEnd.creditsClosed = false;
|
||||
GameEnd.creditsEverClosed = false;
|
||||
// We set this ASAP so that the AD tab is immediately recreated without END formatting, and any lag which could
|
||||
// happen is instead hidden by the overlay from the credits rollback
|
||||
player.celestials.pelle.doomed = false;
|
||||
const backUpOptions = JSON.stringify(player.options);
|
||||
// This can't be JSONed as it contains sets
|
||||
const secretUnlocks = player.secretUnlocks;
|
||||
@ -23,7 +26,7 @@ export const NG = {
|
||||
player.reality.automator.scripts = JSON.parse(automatorScripts);
|
||||
ui.view.newUI = player.options.newUI;
|
||||
ui.view.news = player.options.news.enabled;
|
||||
Themes.find(player.options.theme).set();
|
||||
Themes.find(Theme.currentName()).set();
|
||||
Notations.all.find(n => n.name === player.options.notation).setAsCurrent();
|
||||
ADNotations.Settings.exponentCommas.show = player.options.commas;
|
||||
player.lastUpdate = Date.now();
|
||||
|
@ -61,7 +61,7 @@ class NormalChallengeState extends GameMechanicState {
|
||||
}
|
||||
|
||||
get isDisabled() {
|
||||
return this.config.isDisabledInDoomed && Pelle.isDoomed;
|
||||
return Pelle.isDoomed;
|
||||
}
|
||||
|
||||
get lockedAt() {
|
||||
@ -81,9 +81,10 @@ class NormalChallengeState extends GameMechanicState {
|
||||
start() {
|
||||
if (this.id === 1 || this.isOnlyActiveChallenge) return;
|
||||
if (!Tab.challenges.isUnlocked) return;
|
||||
// Forces big crunch reset but ensures IP gain, if any.
|
||||
bigCrunchReset(true, true);
|
||||
player.challenge.normal.current = this.id;
|
||||
player.challenge.infinity.current = 0;
|
||||
bigCrunchResetValues();
|
||||
if (Enslaved.isRunning && EternityChallenge(6).isRunning && this.id === 10) {
|
||||
EnslavedProgress.challengeCombo.giveProgress();
|
||||
Enslaved.quotes.ec6C10.show();
|
||||
@ -102,6 +103,12 @@ class NormalChallengeState extends GameMechanicState {
|
||||
// and thus unlocking an autobuyer.
|
||||
Achievement(52).tryUnlock();
|
||||
Achievement(53).tryUnlock();
|
||||
|
||||
// Completing a challenge unlocks an autobuyer even if not purchased with antimatter, but we still
|
||||
// need to clear the notification because otherwise it sticks there forever. Any other methods of
|
||||
// unlocking autobuyers (such as Existentially Prolong) should also go through this code path
|
||||
TabNotification.newAutobuyer.clearTrigger();
|
||||
GameCache.cheapestAntimatterAutobuyer.invalidate();
|
||||
}
|
||||
|
||||
get goal() {
|
||||
@ -116,15 +123,14 @@ class NormalChallengeState extends GameMechanicState {
|
||||
if (bestTimes[this.id - 2] <= player.records.thisInfinity.time) {
|
||||
return;
|
||||
}
|
||||
// TODO: remove splice once player.challenge.infinity.bestTimes is not reactive
|
||||
bestTimes.splice(this.id - 2, 1, player.records.thisInfinity.time);
|
||||
player.challenge.normal.bestTimes[this.id - 2] = player.records.thisInfinity.time;
|
||||
GameCache.challengeTimeSum.invalidate();
|
||||
GameCache.worstChallengeTime.invalidate();
|
||||
}
|
||||
|
||||
exit() {
|
||||
player.challenge.normal.current = 0;
|
||||
bigCrunchResetValues();
|
||||
bigCrunchReset(true, false);
|
||||
if (!Enslaved.isRunning) Tab.dimensions.antimatter.show();
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,32 @@
|
||||
const Payments = {
|
||||
interval: null,
|
||||
windowReference: null,
|
||||
// This is here to prevent notification spam; purchase canceling can be called multiple times before the first
|
||||
// call's Promise is settled
|
||||
hasCanceled: false,
|
||||
init: () => {
|
||||
// We have unfinished checkouts
|
||||
// We have unfinished checkouts from when the page was last closed
|
||||
if (player.IAP.checkoutSession.id) {
|
||||
Payments.pollForPurchases();
|
||||
}
|
||||
},
|
||||
|
||||
// Only called from clicking the "Buy More" button in the Shop tab
|
||||
buyMoreSTD: async STD => {
|
||||
player.IAP.checkoutSession = { id: true };
|
||||
const res = await fetch("https://antimatterdimensionspayments.ew.r.appspot.com/purchase", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({ amount: STD })
|
||||
});
|
||||
let res;
|
||||
try {
|
||||
res = await fetch(`${STD_BACKEND_URL}/purchase`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({ amount: STD, cloudID: Cloud.user.id })
|
||||
});
|
||||
} catch (e) {
|
||||
GameUI.notify.error("Could not contact payment server!", 10000);
|
||||
return;
|
||||
}
|
||||
const data = await res.json();
|
||||
Payments.windowReference = window.open(
|
||||
data.url,
|
||||
@ -26,25 +37,35 @@ const Payments = {
|
||||
GameStorage.save();
|
||||
Payments.pollForPurchases();
|
||||
},
|
||||
|
||||
// Starts a purchase-checking loop and adds a listener which cancels any ongoing purchases if the page is closed.
|
||||
// Any unresolved purchases will be reopened when the page is opened again in init()
|
||||
pollForPurchases: () => {
|
||||
console.log("Polling for purchases...");
|
||||
const { id, amount } = player.IAP.checkoutSession;
|
||||
let pollAmount = 0;
|
||||
window.onbeforeunload = async() => {
|
||||
if (!Payments.interval) return;
|
||||
Payments.windowReference?.close();
|
||||
await Payments.cancelPurchase();
|
||||
await Payments.cancelPurchase(false);
|
||||
};
|
||||
|
||||
// This setInterval checks every 3 seconds for a response from the payment backend
|
||||
Payments.interval = setInterval(async() => {
|
||||
pollAmount++;
|
||||
const statusRes = await fetch(
|
||||
`https://antimatterdimensionspayments.ew.r.appspot.com/validate?sessionId=${id}`
|
||||
);
|
||||
let statusRes;
|
||||
try {
|
||||
statusRes = await fetch(`${STD_BACKEND_URL}/validate?sessionId=${id}`);
|
||||
} catch (e) {
|
||||
// Note: Not redundant with notification in buyMoreSTD above; will not be reached if exception is thrown there
|
||||
GameUI.notify.error("Could not contact payment server!", 10000);
|
||||
Payments.clearInterval();
|
||||
return;
|
||||
}
|
||||
const { completed, failure } = await statusRes.json();
|
||||
|
||||
if (completed) {
|
||||
Payments.windowReference?.close();
|
||||
player.IAP.totalSTD += amount;
|
||||
await ShopPurchaseData.syncSTD();
|
||||
GameUI.notify.success(`Purchase of ${amount} STDs was successful, thank you for your support! ❤️`, 10000);
|
||||
Payments.clearInterval();
|
||||
player.IAP.checkoutSession = { id: false };
|
||||
@ -52,7 +73,6 @@ const Payments = {
|
||||
Modal.hide();
|
||||
}
|
||||
|
||||
|
||||
if (failure) {
|
||||
Payments.windowReference?.close();
|
||||
Payments.clearInterval();
|
||||
@ -64,24 +84,59 @@ const Payments = {
|
||||
|
||||
// 30 minutes of polling is the maximum
|
||||
if (!completed && (Payments.windowReference?.closed || pollAmount >= 20 * 30)) {
|
||||
await Payments.cancelPurchase();
|
||||
await Payments.cancelPurchase(true);
|
||||
}
|
||||
}, 3000);
|
||||
},
|
||||
async cancelPurchase() {
|
||||
|
||||
// Sends a request to purchase a STD upgrade, returning true if successful (and syncs data), false if not
|
||||
async buyUpgrade(upgradeKey) {
|
||||
if (!Cloud.loggedIn) return false;
|
||||
let res;
|
||||
try {
|
||||
res = await fetch(`${STD_BACKEND_URL}/upgrade`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({ user: Cloud.user.id, upgrade: upgradeKey })
|
||||
});
|
||||
} catch (e) {
|
||||
GameUI.notify.error("Unable to spend STD coins on upgrade!", 10000);
|
||||
return false;
|
||||
}
|
||||
const stdData = await res.json();
|
||||
// The "not enough STDs" message should only show up if the player modifies costs on the frontend and forces the
|
||||
// game to send a request despite not actually having enough STDs. The cost check is done again on the backend
|
||||
if (stdData.success) GameUI.notify.info(`Successfully spent ${stdData.amountSpent} STD coins`, 10000);
|
||||
else GameUI.notify.error("Not enough STDs to purchase upgrade!", 10000);
|
||||
ShopPurchaseData.syncSTD(false, stdData.data);
|
||||
return stdData.success;
|
||||
},
|
||||
|
||||
// Explicitly cancels purchases if the player chooses to, they take too long to resolve, or the page is closed
|
||||
async cancelPurchase(isTimeout) {
|
||||
if (this.hasCanceled) return;
|
||||
Payments.windowReference?.close();
|
||||
Payments.clearInterval();
|
||||
await fetch("https://antimatterdimensionspayments.ew.r.appspot.com/expire", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({ sessionId: player.IAP.checkoutSession.id })
|
||||
});
|
||||
GameUI.notify.error(`Purchase failed!`, 10000);
|
||||
try {
|
||||
await fetch(`${STD_BACKEND_URL}/expire`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({ sessionId: player.IAP.checkoutSession.id })
|
||||
});
|
||||
} catch (e) {
|
||||
GameUI.notify.error("Could not contact payment server!", 10000);
|
||||
}
|
||||
if (isTimeout) GameUI.notify.error("Purchase took too long to resolve!", 10000);
|
||||
player.IAP.checkoutSession = { id: false };
|
||||
GameStorage.save();
|
||||
this.hasCanceled = false;
|
||||
},
|
||||
|
||||
// Removes the repeating checker and page-close listener for if payments have been resolved
|
||||
clearInterval() {
|
||||
clearInterval(Payments.interval);
|
||||
window.onbeforeunload = null;
|
||||
|
@ -192,7 +192,7 @@ window.player = {
|
||||
},
|
||||
annihilation: {
|
||||
isActive: false,
|
||||
multiplier: 5
|
||||
multiplier: 1.05,
|
||||
},
|
||||
singularity: { isActive: false },
|
||||
ipMultBuyer: { isActive: false, },
|
||||
@ -267,6 +267,7 @@ window.player = {
|
||||
records: {
|
||||
gameCreatedTime: Date.now(),
|
||||
totalTimePlayed: 0,
|
||||
timePlayedAtBHUnlock: Number.MAX_VALUE,
|
||||
realTimePlayed: 0,
|
||||
realTimeDoomed: 0,
|
||||
totalAntimatter: DC.E1,
|
||||
@ -334,8 +335,10 @@ window.player = {
|
||||
isUnlocked: false,
|
||||
isActive: false,
|
||||
isSegmented: false,
|
||||
usedSTD: false,
|
||||
hasStarted: false,
|
||||
hideInfo: false,
|
||||
displayAllMilestones: false,
|
||||
startDate: 0,
|
||||
name: "",
|
||||
offlineTimeUsed: 0,
|
||||
@ -767,9 +770,12 @@ window.player = {
|
||||
retryCelestial: false,
|
||||
showAllChallenges: false,
|
||||
cloudEnabled: true,
|
||||
showCloudModal: true,
|
||||
forceCloudOverwrite: false,
|
||||
syncSaveIntervals: true,
|
||||
hotkeys: true,
|
||||
theme: "Normal",
|
||||
themeClassic: "Normal",
|
||||
themeModern: "Normal",
|
||||
commas: true,
|
||||
updateRate: 33,
|
||||
newUI: true,
|
||||
@ -863,6 +869,7 @@ window.player = {
|
||||
hiddenSubtabBits: Array.repeat(0, 11),
|
||||
lastOpenTab: 0,
|
||||
lastOpenSubtab: Array.repeat(0, 11),
|
||||
currentMultiplierSubtab: 0,
|
||||
fixedPerkStartingPos: false,
|
||||
perkPhysicsEnabled: true,
|
||||
automatorEvents: {
|
||||
@ -874,16 +881,7 @@ window.player = {
|
||||
}
|
||||
},
|
||||
IAP: {
|
||||
totalSTD: 0,
|
||||
spentSTD: 0,
|
||||
IPPurchases: 0,
|
||||
EPPurchases: 0,
|
||||
RMPurchases: 0,
|
||||
dimPurchases: 0,
|
||||
allDimPurchases: 0,
|
||||
replicantiPurchases: 0,
|
||||
dilatedTimePurchases: 0,
|
||||
disabled: false,
|
||||
enabled: false,
|
||||
checkoutSession: {
|
||||
id: false,
|
||||
}
|
||||
@ -1012,7 +1010,6 @@ export function guardFromNaNValues(obj) {
|
||||
for (const key in obj) {
|
||||
if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
|
||||
|
||||
// TODO: rework autobuyer saving
|
||||
if (key === "automator") continue;
|
||||
|
||||
let value = obj[key];
|
||||
|
@ -75,10 +75,13 @@ export const GlyphSelection = {
|
||||
},
|
||||
|
||||
select(glyphID, sacrifice) {
|
||||
const chosenGlyph = this.glyphs[glyphID];
|
||||
if (sacrifice) {
|
||||
GlyphSacrificeHandler.removeGlyph(this.glyphs[glyphID], true);
|
||||
GlyphSacrificeHandler.removeGlyph(chosenGlyph, true);
|
||||
} else if (GameCache.glyphInventorySpace.value > 0) {
|
||||
Glyphs.addToInventory(chosenGlyph);
|
||||
} else {
|
||||
Glyphs.addToInventory(this.glyphs[glyphID]);
|
||||
AutoGlyphProcessor.getRidOfGlyph(chosenGlyph);
|
||||
}
|
||||
this.glyphs = [];
|
||||
this.realityProps = undefined;
|
||||
@ -113,11 +116,20 @@ export function requestManualReality() {
|
||||
return;
|
||||
}
|
||||
if (GameCache.glyphInventorySpace.value === 0) {
|
||||
Modal.message.show("Inventory cannot hold new Glyphs. Delete/sacrifice (shift-click) some Glyphs.",
|
||||
Modal.message.show("No available inventory space; free up space by shift-clicking Glyphs to get rid of them.",
|
||||
{ closeEvent: GAME_EVENT.GLYPHS_CHANGED });
|
||||
return;
|
||||
}
|
||||
processManualReality(false);
|
||||
startManualReality(false);
|
||||
}
|
||||
|
||||
export function startManualReality(sacrifice, glyphID) {
|
||||
if (player.options.animations.reality) {
|
||||
runRealityAnimation();
|
||||
setTimeout(processManualReality, 3000, sacrifice, glyphID);
|
||||
} else {
|
||||
processManualReality(sacrifice, glyphID);
|
||||
}
|
||||
}
|
||||
|
||||
export function processManualReality(sacrifice, glyphID) {
|
||||
@ -167,16 +179,8 @@ export function processManualReality(sacrifice, glyphID) {
|
||||
|
||||
// We've already gotten a glyph at this point, so the second value has to be true.
|
||||
// If we haven't sacrificed, we need to sort and purge glyphs, as applicable.
|
||||
triggerManualReality(getRealityProps(false, true));
|
||||
}
|
||||
beginProcessReality(getRealityProps(false, true));
|
||||
|
||||
function triggerManualReality(realityProps) {
|
||||
if (player.options.animations.reality) {
|
||||
runRealityAnimation();
|
||||
setTimeout(beginProcessReality, 3000, realityProps);
|
||||
} else {
|
||||
beginProcessReality(realityProps);
|
||||
}
|
||||
// Should be here so that the perk graphics update even when we're on the perk subtab, while also keeping its
|
||||
// relatively expensive operations off of the reality reset hot path for when realities are significantly faster
|
||||
PerkNetwork.updatePerkColor();
|
||||
@ -207,19 +211,20 @@ function processAutoGlyph(gainedLevel, rng) {
|
||||
// Always generate a list of glyphs to avoid RNG diverging based on whether
|
||||
// a reality is done automatically.
|
||||
const glyphs = GlyphSelection.glyphList(GlyphSelection.choiceCount, gainedLevel, { rng });
|
||||
let keepGlyph;
|
||||
if (EffarigUnlock.glyphFilter.isUnlocked) {
|
||||
newGlyph = AutoGlyphProcessor.pick(glyphs);
|
||||
if (!AutoGlyphProcessor.wouldKeep(newGlyph) || GameCache.glyphInventorySpace.value === 0) {
|
||||
AutoGlyphProcessor.getRidOfGlyph(newGlyph);
|
||||
newGlyph = null;
|
||||
}
|
||||
keepGlyph = AutoGlyphProcessor.wouldKeep(newGlyph);
|
||||
} else {
|
||||
// It really doesn't matter which we pick since they're random,
|
||||
// so we might as well take the first one.
|
||||
newGlyph = glyphs[0];
|
||||
keepGlyph = true;
|
||||
}
|
||||
if (newGlyph && GameCache.glyphInventorySpace.value > 0) {
|
||||
if (keepGlyph && GameCache.glyphInventorySpace.value > 0) {
|
||||
Glyphs.addToInventory(newGlyph);
|
||||
} else {
|
||||
AutoGlyphProcessor.getRidOfGlyph(newGlyph);
|
||||
}
|
||||
}
|
||||
|
||||
@ -541,7 +546,9 @@ export function finishProcessReality(realityProps) {
|
||||
disChargeAll();
|
||||
}
|
||||
}
|
||||
if (AutomatorBackend.state.forceRestart) AutomatorBackend.restart();
|
||||
if (Player.automatorUnlocked && AutomatorBackend.state.forceRestart) {
|
||||
AutomatorBackend.start(player.reality.automator.state.editorScript);
|
||||
}
|
||||
if (player.options.automatorEvents.clearOnReality) AutomatorData.clearEventLog();
|
||||
|
||||
const celestialRunState = clearCelestialRuns();
|
||||
@ -627,6 +634,7 @@ export function finishProcessReality(realityProps) {
|
||||
player.dilation.lastEP = DC.DM1;
|
||||
Currency.antimatter.reset();
|
||||
Enslaved.autoReleaseTick = 0;
|
||||
player.celestials.enslaved.hasSecretStudy = false;
|
||||
player.celestials.laitela.entropy = 0;
|
||||
|
||||
playerInfinityUpgradesOnReset();
|
||||
@ -636,7 +644,7 @@ export function finishProcessReality(realityProps) {
|
||||
fullResetTimeDimensions();
|
||||
resetChallengeStuff();
|
||||
AntimatterDimensions.reset();
|
||||
secondSoftReset();
|
||||
secondSoftReset(false);
|
||||
player.celestials.ra.peakGamespeed = 1;
|
||||
|
||||
InfinityDimensions.resetAmount();
|
||||
|
@ -764,9 +764,10 @@ 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]);
|
||||
for (let i = 0; i < infinities.length - 1; i++) {
|
||||
if (infinities[i].lt(infinities[i + 1].times(Decimal.NUMBER_MAX_VALUE)) || infinities[i].eq(0)) return false;
|
||||
if (infinities[i].lt(infinities[i + 1].times(Decimal.NUMBER_MAX_VALUE))) return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
@ -1039,9 +1040,10 @@ 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]);
|
||||
for (let i = 0; i < eternities.length - 1; i++) {
|
||||
if (eternities[i].lt(eternities[i + 1].times(Decimal.NUMBER_MAX_VALUE)) || eternities[i].eq(0)) return false;
|
||||
if (eternities[i].lt(eternities[i + 1].times(Decimal.NUMBER_MAX_VALUE))) return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
@ -1813,8 +1813,8 @@ GameDatabase.celestials.navigation = {
|
||||
complete: () => {
|
||||
if (Pelle.isUnlocked) return 1;
|
||||
const imCost = Math.clampMax(emphasizeEnd(Math.log10(Currency.imaginaryMachines.value) / Math.log10(1.6e15)), 1);
|
||||
let laitelaProgress = Laitela.isRunning ? Currency.eternityPoints.value.log10() / 4000 : 0;
|
||||
if (Laitela.difficultyTier !== 8) laitelaProgress = 0;
|
||||
let laitelaProgress = Laitela.isRunning ? Math.min(Currency.eternityPoints.value.log10() / 4000, 0.99) : 0;
|
||||
if (Laitela.difficultyTier !== 8 || Glyphs.activeList.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) {
|
||||
if (!Laitela.isRunning || Laitela.difficultyTier !== 8 || Glyphs.activeList.length > 1) {
|
||||
laitelaString = "Lai'tela's Reality is still intact";
|
||||
} else if (ImaginaryUpgrade(25).isAvailableForPurchase) {
|
||||
laitelaString = "Lai'tela's Reality has been destroyed";
|
||||
@ -1888,8 +1888,8 @@ GameDatabase.celestials.navigation = {
|
||||
Achievements.prePelleRows.length];
|
||||
const alchemy = [AlchemyResources.all.countWhere(r => r.capped), AlchemyResources.all.length];
|
||||
return [
|
||||
`Complete ${formatInt(achievements[0])} / ${formatInt(achievements[1])} rows of achievements`,
|
||||
`Fill ${formatInt(alchemy[0])} / ${formatInt(alchemy[1])} alchemy resources`,
|
||||
`Complete ${formatInt(achievements[0])} / ${formatInt(achievements[1])} rows of Achievements`,
|
||||
`Fill ${formatInt(alchemy[0])} / ${formatInt(alchemy[1])} Alchemy Resources`,
|
||||
];
|
||||
},
|
||||
angle: 290,
|
||||
|
@ -108,13 +108,22 @@ GameDatabase.celestials.singularityMilestones = {
|
||||
effectFormat: x => ((x === 0) ? "No autobuyers" : `Autobuy up to the ${["1st", "2nd", "3rd", "4th"][x - 1]} DMD`),
|
||||
upgradeDirection: LAITELA_UPGRADE_DIRECTION.SELF_BOOST,
|
||||
},
|
||||
ascensionAutobuyers: {
|
||||
start: 1e8,
|
||||
repeat: 140,
|
||||
limit: 4,
|
||||
description: "DMD Ascension Autobuyers",
|
||||
effect: completions => completions,
|
||||
effectFormat: x => ((x === 0) ? "No autobuyers" : `Ascend up to the ${["1st", "2nd", "3rd", "4th"][x - 1]} DMD`),
|
||||
upgradeDirection: LAITELA_UPGRADE_DIRECTION.SELF_BOOST,
|
||||
},
|
||||
darkAutobuyerSpeed: {
|
||||
start: 45,
|
||||
repeat: 650,
|
||||
limit: 8,
|
||||
description: "All Dark Matter Dimension Autobuyers trigger faster",
|
||||
description: "Autobuyer speed for all DMD Autobuyers",
|
||||
effect: completions => [30, 20, 15, 10, 5, 3, 2, 1, 0][completions],
|
||||
effectFormat: x => `${formatInt(x)}s`,
|
||||
effectFormat: x => (x === 0 ? "Instant" : `${formatInt(x)}s`),
|
||||
upgradeDirection: LAITELA_UPGRADE_DIRECTION.SELF_BOOST,
|
||||
},
|
||||
realityDEMultiplier: {
|
||||
@ -204,7 +213,7 @@ GameDatabase.celestials.singularityMilestones = {
|
||||
start: 5e11,
|
||||
repeat: 0,
|
||||
limit: 1,
|
||||
description: "Annihilation multiplier generates 4th Dark Matter Dimensions when Annihilation is available",
|
||||
description: "Annihilation mult. generates 4th DMD when Annihilation is available",
|
||||
effect: () => Laitela.darkMatterMult,
|
||||
effectFormat: x => `${format(x, 2, 1)}/s`,
|
||||
upgradeDirection: LAITELA_UPGRADE_DIRECTION.SELF_BOOST,
|
||||
@ -218,6 +227,15 @@ GameDatabase.celestials.singularityMilestones = {
|
||||
effectFormat: x => formatX(x, 2, 2),
|
||||
upgradeDirection: LAITELA_UPGRADE_DIRECTION.SELF_BOOST,
|
||||
},
|
||||
annihilationAutobuyer: {
|
||||
start: 4e18,
|
||||
repeat: 0,
|
||||
limit: 1,
|
||||
description: "Unlock an Autobuyer for Annihilation",
|
||||
effect: completions => completions,
|
||||
effectFormat: x => (x === 1 ? "Unlocked" : "Locked"),
|
||||
upgradeDirection: LAITELA_UPGRADE_DIRECTION.SELF_BOOST,
|
||||
},
|
||||
theoremPowerFromSingularities: {
|
||||
start: 3e21,
|
||||
repeat: 0,
|
||||
|
@ -16,7 +16,9 @@ GameDatabase.challenges.eternity = [
|
||||
effect: completions =>
|
||||
Decimal.pow(Math.max(player.records.thisEternity.time / 10, 0.9), 0.3 + (completions * 0.05)),
|
||||
formatEffect: value => formatX(value, 2, 1)
|
||||
}
|
||||
},
|
||||
// These will get notation-formatted and scrambled between for the final goal
|
||||
scrambleText: ["1e2600", "1e201600"],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
@ -77,14 +79,9 @@ GameDatabase.challenges.eternity = [
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
// The asterisk, if present, will get replaced with strings generated from the scramble text
|
||||
description: () => {
|
||||
if (Enslaved.isRunning) {
|
||||
return Notations.current === Notation.shi
|
||||
? "yo̶u̶ ̶c̶a̶n̶not̶ ̶g̶a̶i̶n̶ ̶A̶n̶t̶i̶m̶a̶t̶t̶e̶r̶ ̶G̶a̶l̶a̶x̶i̶e̶s̶ ̶n̶o̶r̶m̶a̶l̶l̶y̶. ̶ ̶ ̶The " +
|
||||
" cost of upgrading your max Replicanti Galaxies is massively reduced."
|
||||
: "you c㏰'퐚 gai鸭 Antmatterﻪﶓa⁍axie㮾랜䂇rma㦂l the cost of upgrading your max Replicanti" +
|
||||
" Galaxies is massively reduced";
|
||||
}
|
||||
if (Enslaved.isRunning) return "you *. The cost of upgrading your max Replicanti Galaxies is massively reduced.";
|
||||
return "you cannot gain Antimatter Galaxies normally. The cost of upgrading your max Replicanti" +
|
||||
" Galaxies is massively reduced.";
|
||||
},
|
||||
@ -98,7 +95,8 @@ GameDatabase.challenges.eternity = [
|
||||
const total = Math.round(Player.dimensionMultDecrease + Effects.sum(EternityChallenge(6).reward)) - value;
|
||||
return `-${format(value, 2, 1)} (${formatX(total, 2, 1)} total)`;
|
||||
}
|
||||
}
|
||||
},
|
||||
scrambleText: ["cannot gain Antimatter Galaxies normally", "c㏰'퐚 gai鸭 Antiat랜erﻪﶓa⁍axie㮾 䂇orma㦂l"],
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
|
@ -19,7 +19,6 @@ GameDatabase.challenges.normal = [
|
||||
name: "1st Antimatter Dimension Autobuyer",
|
||||
reward: "Upgradeable 1st Antimatter Dimension Autobuyer",
|
||||
lockedAt: DC.D0,
|
||||
isDisabledInDoomed: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
@ -31,7 +30,6 @@ GameDatabase.challenges.normal = [
|
||||
name: "2nd Antimatter Dimension Autobuyer",
|
||||
reward: "Upgradeable 2nd Antimatter Dimension Autobuyer",
|
||||
lockedAt: DC.D0,
|
||||
isDisabledInDoomed: true,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
@ -43,7 +41,6 @@ GameDatabase.challenges.normal = [
|
||||
name: "3rd Antimatter Dimension",
|
||||
reward: "Upgradeable 3rd Antimatter Dimension Autobuyer",
|
||||
lockedAt: DC.D0,
|
||||
isDisabledInDoomed: true,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
@ -54,7 +51,6 @@ GameDatabase.challenges.normal = [
|
||||
name: "4th Antimatter Dimension Autobuyer",
|
||||
reward: "Upgradeable 4th Antimatter Dimension Autobuyer",
|
||||
lockedAt: DC.D0,
|
||||
isDisabledInDoomed: true,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
@ -65,7 +61,6 @@ GameDatabase.challenges.normal = [
|
||||
name: "5th Antimatter Dimension Autobuyer",
|
||||
reward: "Upgradeable 5th Antimatter Dimension Autobuyer",
|
||||
lockedAt: DC.D0,
|
||||
isDisabledInDoomed: true,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
@ -76,7 +71,6 @@ GameDatabase.challenges.normal = [
|
||||
name: "6th Antimatter Dimension Autobuyer",
|
||||
reward: "Upgradeable 6th Antimatter Dimension Autobuyer",
|
||||
lockedAt: DC.D0,
|
||||
isDisabledInDoomed: true,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
@ -88,7 +82,6 @@ GameDatabase.challenges.normal = [
|
||||
name: "7th Antimatter Dimension Autobuyer",
|
||||
reward: "Upgradeable 7th Antimatter Dimension Autobuyer",
|
||||
lockedAt: DC.D0,
|
||||
isDisabledInDoomed: true,
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
@ -99,7 +92,6 @@ GameDatabase.challenges.normal = [
|
||||
name: "8th Antimatter Dimension Autobuyer",
|
||||
reward: "Upgradeable 8th Antimatter Dimension Autobuyer",
|
||||
lockedAt: DC.D0,
|
||||
isDisabledInDoomed: true,
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
@ -110,7 +102,6 @@ GameDatabase.challenges.normal = [
|
||||
name: "Tickspeed Autobuyer",
|
||||
reward: "Upgradeable Tickspeed Autobuyer",
|
||||
lockedAt: DC.D0,
|
||||
isDisabledInDoomed: true,
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
@ -121,7 +112,6 @@ GameDatabase.challenges.normal = [
|
||||
name: "Automated Dimension Boosts",
|
||||
reward: "Dimension Boosts Autobuyer",
|
||||
lockedAt: DC.D16,
|
||||
isDisabledInDoomed: true,
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
@ -132,7 +122,6 @@ GameDatabase.challenges.normal = [
|
||||
name: "Automated Antimatter Galaxies",
|
||||
reward: "Antimatter Galaxies Autobuyer",
|
||||
lockedAt: DC.D16,
|
||||
isDisabledInDoomed: true,
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
|
@ -88,7 +88,7 @@ GameDatabase.eternity.dilation = {
|
||||
doubleGalaxies: {
|
||||
id: 4,
|
||||
cost: 5e6,
|
||||
description: () => `Gain twice as many Tachyon Galaxies, up to ${formatInt(1000)}`,
|
||||
description: () => `Gain twice as many Tachyon Galaxies, up to ${formatInt(500)} base Galaxies`,
|
||||
effect: 2
|
||||
},
|
||||
tdMultReplicanti: {
|
||||
|
@ -27,10 +27,13 @@ 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);
|
||||
if (!player.options.offlineProgress) return `This milestone would give offline EP generation, but offline progress
|
||||
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. (Currently ${format(EPmin, 2, 2)} EP/min)`;
|
||||
Eternities. (${effectText})`;
|
||||
},
|
||||
activeCondition: () => (player.options.offlineProgress
|
||||
? `Active as long as neither of the other offline milestones
|
||||
@ -139,10 +142,10 @@ GameDatabase.eternity.milestones = {
|
||||
// which seems messy to say the least.
|
||||
// eslint-disable-next-line prefer-template
|
||||
return `While offline, gain Eternities at ${formatPercents(0.5)} the rate of your fastest Eternity. ` +
|
||||
(eternities.gt(0) ? `(currently ${format(eternities, 2, 2)}/hour)` : "(disabled)");
|
||||
(eternities.gt(0) ? `(Currently ${format(eternities, 2, 2)}/hour)` : "(Inactive)");
|
||||
},
|
||||
activeCondition: () => (player.options.offlineProgress
|
||||
? `Must be outside of all Challenges and Dilation
|
||||
? `Must be outside of all Challenges and Dilation,
|
||||
and the Eternity Autobuyer must be turned on and set to zero EP.`
|
||||
: ""),
|
||||
},
|
||||
@ -155,7 +158,7 @@ GameDatabase.eternity.milestones = {
|
||||
// eslint-disable-next-line prefer-template
|
||||
return `While offline, gain Infinities equal to ${formatPercents(0.5)}
|
||||
your best Infinities/hour this Eternity. ` +
|
||||
(infinities.gt(0) ? `(currently ${format(infinities, 2, 2)}/hour)` : "(disabled)");
|
||||
(infinities.gt(0) ? `(Currently ${format(infinities, 2, 2)}/hour)` : "(Inactive)");
|
||||
},
|
||||
activeCondition: () => (player.options.offlineProgress
|
||||
? `Must be outside of Normal/Infinity Challenges and outside of EC4 and EC12,
|
||||
|
@ -119,7 +119,7 @@ autobuyers - in this situation autobuyers will effectively only trigger once eve
|
||||
may have a strong impact depending on the part of the game.
|
||||
<br>
|
||||
<br>
|
||||
${BlackHole(1).isUnlocked
|
||||
${player.blackHole[0].unlocked
|
||||
? `<b>Offline Black Hole behavior:</b> Once the Black Hole has been unlocked, the offline progress simulation will
|
||||
attempt to run the game in a way where each tick contains roughly the same amount of <i>game</i> time. This may
|
||||
give the appearance of the Black Hole(s) being active for a much larger fraction of time than normal while
|
||||
@ -1048,8 +1048,8 @@ Duration - How long each speed burst lasts before going back to normal speed,
|
||||
increased by ${formatPercents(0.3)} per upgrade.
|
||||
<br>
|
||||
<br>
|
||||
Once you have ${formatInt(1)} year of <i>game time</i> on your save, you unlock a Reality Upgrade that allows
|
||||
you to have a second Black Hole.
|
||||
${formatInt(100)} days of <i>game time</i> after unlocking the Black Hole, you unlock the ability to purchase
|
||||
a Reality Upgrade that allows you to have a second Black Hole.
|
||||
The timer on the second Black Hole only advances when the first Black Hole is active. So, for example, if the first
|
||||
Black Hole has a duration of ${formatInt(4)} minutes and the second has an interval of ${formatInt(8)} minutes, the
|
||||
second Black Hole will only activate once every two cycles of the first Black Hole regardless of how short the
|
||||
@ -1081,7 +1081,7 @@ the Black Hole tab.
|
||||
<br>
|
||||
<b>Hotkey: B</b> will pause/unpause the Black Holes.
|
||||
`,
|
||||
isUnlocked: () => BlackHole(1).isUnlocked,
|
||||
isUnlocked: () => player.blackHole[0].unlocked,
|
||||
tags: ["reality", "time", "speed", "duration", "interval", "rm", "endgame", "lategame"],
|
||||
tab: "reality/hole"
|
||||
}, {
|
||||
@ -1292,7 +1292,7 @@ The Nameless Ones won't directly unlock the next Celestial.
|
||||
// TODO Add the rest of the testers here too before release; this is all only pre wave 1
|
||||
tags: ["reality", "time", "blackhole", "lategame", "endgame", "testers", "celestial",
|
||||
"ikerstream", "realrapidjazz", "saturnus", "earth", "garnet", "pichusuperlover"],
|
||||
tab: "celestials/nameless"
|
||||
tab: "celestials/enslaved"
|
||||
}, {
|
||||
name: "Tesseracts",
|
||||
info: () => `
|
||||
|
@ -48,6 +48,8 @@ export * from "./celestials/singularity-milestones";
|
||||
import "./script-templates";
|
||||
import "./speedrun-milestones";
|
||||
|
||||
import "./multiplier-tab/index";
|
||||
|
||||
import "./celestials/quotes/index";
|
||||
|
||||
import "./h2p";
|
||||
|
@ -0,0 +1,416 @@
|
||||
import { DC } from "../../constants";
|
||||
import { GameDatabase } from "../game-database";
|
||||
import { PlayerProgress } from "../../app/player-progress";
|
||||
|
||||
import { MultiplierTabHelper } from "./helper-functions";
|
||||
import { MultiplierTabIcons } from "./icons";
|
||||
|
||||
// See index.js for documentation
|
||||
GameDatabase.multiplierTabValues.AD = {
|
||||
total: {
|
||||
name: dim => (dim ? `AD ${dim} Multiplier` : "Base AD Production"),
|
||||
displayOverride: dim => {
|
||||
if (dim) return formatX(AntimatterDimension(dim).multiplier, 2, 2);
|
||||
const highestDim = AntimatterDimension(
|
||||
EternityChallenge(7).isRunning ? 7 : MultiplierTabHelper.activeDimCount("AD")).totalAmount;
|
||||
return `${format(AntimatterDimensions.all
|
||||
.filter(ad => ad.isProducing)
|
||||
.map(ad => ad.multiplier)
|
||||
.reduce((x, y) => x.times(y), DC.D1)
|
||||
.times(highestDim), 2)}/sec`;
|
||||
},
|
||||
multValue: dim => {
|
||||
const mult = dim
|
||||
? AntimatterDimension(dim).multiplier
|
||||
: AntimatterDimensions.all
|
||||
.filter(ad => ad.isProducing)
|
||||
.map(ad => ad.multiplier)
|
||||
.reduce((x, y) => x.times(y), DC.D1);
|
||||
const highestDim = AntimatterDimension(
|
||||
EternityChallenge(7).isRunning ? 7 : MultiplierTabHelper.activeDimCount("AD")).totalAmount;
|
||||
return mult.times(highestDim).clampMin(1);
|
||||
},
|
||||
isActive: dim => (dim ? dim <= MultiplierTabHelper.activeDimCount("AD") : true),
|
||||
dilationEffect: () => {
|
||||
const baseEff = (player.dilation.active || Enslaved.isRunning)
|
||||
? 0.75 * Effects.product(DilationUpgrade.dilationPenalty)
|
||||
: 1;
|
||||
return baseEff * (Effarig.isRunning ? Effarig.multDilation : 1);
|
||||
},
|
||||
isDilated: true,
|
||||
overlay: ["Ω", "<i class='fas fa-cube' />"],
|
||||
icon: dim => MultiplierTabIcons.DIMENSION("AD", dim),
|
||||
},
|
||||
purchase: {
|
||||
name: dim => (dim ? `Purchased AD ${dim}` : "Purchases"),
|
||||
multValue: dim => {
|
||||
const getPurchases = ad => (Laitela.continuumActive
|
||||
? AntimatterDimension(ad).continuumValue
|
||||
: Math.floor(AntimatterDimension(ad).bought / 10)
|
||||
);
|
||||
if (dim) return Decimal.pow(AntimatterDimensions.buyTenMultiplier, getPurchases(dim));
|
||||
return AntimatterDimensions.all
|
||||
.filter(ad => ad.isProducing)
|
||||
.map(ad => Decimal.pow(AntimatterDimensions.buyTenMultiplier, getPurchases(ad.tier)))
|
||||
.reduce((x, y) => x.times(y), DC.D1);
|
||||
},
|
||||
isActive: () => !EternityChallenge(11).isRunning,
|
||||
icon: dim => MultiplierTabIcons.PURCHASE("AD", dim),
|
||||
},
|
||||
highestDim: {
|
||||
name: () => `Amount of highest Dimension`,
|
||||
displayOverride: () => {
|
||||
const dim = EternityChallenge(7).isRunning ? 7 : MultiplierTabHelper.activeDimCount("AD");
|
||||
return `AD ${dim}, ${format(AntimatterDimension(dim).totalAmount, 2)}`;
|
||||
},
|
||||
multValue: () => {
|
||||
const dim = EternityChallenge(7).isRunning ? 7 : MultiplierTabHelper.activeDimCount("AD");
|
||||
return AntimatterDimension(dim).totalAmount;
|
||||
},
|
||||
isActive: () => AntimatterDimension(1).isProducing,
|
||||
icon: MultiplierTabIcons.DIMENSION("AD"),
|
||||
},
|
||||
|
||||
dimboost: {
|
||||
name: dim => (dim ? `Dimboosts on AD ${dim}` : "Dimboosts"),
|
||||
multValue: dim => (dim
|
||||
? DimBoost.multiplierToNDTier(dim)
|
||||
: AntimatterDimensions.all
|
||||
.filter(ad => ad.isProducing)
|
||||
.map(ad => DimBoost.multiplierToNDTier(ad.tier))
|
||||
.reduce((x, y) => x.times(y), DC.D1)),
|
||||
isActive: true,
|
||||
icon: MultiplierTabIcons.DIMBOOST,
|
||||
},
|
||||
sacrifice: {
|
||||
name: "Sacrifice Multiplier",
|
||||
multValue: dim => ((!dim || dim === 8) ? Sacrifice.totalBoost : DC.D1),
|
||||
isActive: dim => (!dim || dim === 8) && Sacrifice.totalBoost.gt(1) && !EternityChallenge(11).isRunning,
|
||||
icon: MultiplierTabIcons.SACRIFICE("antimatter"),
|
||||
},
|
||||
achievementMult: {
|
||||
name: "Achievement Multiplier",
|
||||
multValue: dim => Decimal.pow(Achievements.power, dim ? 1 : MultiplierTabHelper.activeDimCount("AD")),
|
||||
isActive: () => !Pelle.isDoomed && !EternityChallenge(11).isRunning,
|
||||
icon: MultiplierTabIcons.ACHIEVEMENT,
|
||||
},
|
||||
achievement: {
|
||||
name: "Achievement Rewards",
|
||||
multValue: dim => {
|
||||
const allMult = DC.D1.timesEffectsOf(
|
||||
Achievement(48),
|
||||
Achievement(56),
|
||||
Achievement(65),
|
||||
Achievement(72),
|
||||
Achievement(73),
|
||||
Achievement(74),
|
||||
Achievement(76),
|
||||
Achievement(84),
|
||||
Achievement(91),
|
||||
Achievement(92)
|
||||
);
|
||||
|
||||
const dimMults = Array.repeat(DC.D1, 9);
|
||||
for (let tier = 1; tier <= 8; tier++) {
|
||||
if (tier === 1) {
|
||||
dimMults[tier] = dimMults[tier].timesEffectsOf(
|
||||
Achievement(28),
|
||||
Achievement(31),
|
||||
Achievement(68),
|
||||
Achievement(71),
|
||||
);
|
||||
}
|
||||
dimMults[tier] = dimMults[tier].timesEffectsOf(
|
||||
tier === 8 ? Achievement(23) : null,
|
||||
tier < 8 ? Achievement(34) : null,
|
||||
tier <= 4 ? Achievement(64) : null,
|
||||
);
|
||||
if (Achievement(43).isUnlocked) {
|
||||
dimMults[tier] = dimMults[tier].times(1 + tier / 100);
|
||||
}
|
||||
}
|
||||
|
||||
if (dim) return allMult.times(dimMults[dim]);
|
||||
let totalMult = DC.D1;
|
||||
for (let tier = 1; tier <= MultiplierTabHelper.activeDimCount("AD"); tier++) {
|
||||
totalMult = totalMult.times(dimMults[tier]).times(allMult);
|
||||
}
|
||||
return totalMult;
|
||||
},
|
||||
powValue: () => Achievement(183).effectOrDefault(1),
|
||||
isActive: () => !EternityChallenge(11).isRunning,
|
||||
icon: MultiplierTabIcons.ACHIEVEMENT,
|
||||
},
|
||||
infinityUpgrade: {
|
||||
name: dim => (dim ? `Infinity Upgrades (AD ${dim})` : "Infinity Upgrades"),
|
||||
multValue: dim => {
|
||||
const allMult = DC.D1.timesEffectsOf(
|
||||
InfinityUpgrade.totalTimeMult,
|
||||
InfinityUpgrade.thisInfinityTimeMult,
|
||||
);
|
||||
|
||||
const dimMults = Array.repeat(DC.D1, 9);
|
||||
for (let tier = 1; tier <= 8; tier++) {
|
||||
if (tier === 1) {
|
||||
dimMults[tier] = dimMults[tier].timesEffectsOf(
|
||||
InfinityUpgrade.unspentIPMult,
|
||||
InfinityUpgrade.unspentIPMult.chargedEffect,
|
||||
);
|
||||
}
|
||||
dimMults[tier] = dimMults[tier].timesEffectsOf(
|
||||
AntimatterDimension(tier).infinityUpgrade,
|
||||
);
|
||||
}
|
||||
|
||||
if (dim) return allMult.times(dimMults[dim]);
|
||||
let totalMult = DC.D1;
|
||||
for (let tier = 1; tier <= MultiplierTabHelper.activeDimCount("AD"); tier++) {
|
||||
totalMult = totalMult.times(dimMults[tier]).times(allMult);
|
||||
}
|
||||
return totalMult;
|
||||
},
|
||||
powValue: dim => {
|
||||
const allPow = InfinityUpgrade.totalTimeMult.chargedEffect.effectOrDefault(1) *
|
||||
InfinityUpgrade.thisInfinityTimeMult.chargedEffect.effectOrDefault(1);
|
||||
|
||||
const dimPow = Array.repeat(1, 9);
|
||||
for (let tier = 1; tier <= 8; tier++) {
|
||||
dimPow[tier] = AntimatterDimension(tier).infinityUpgrade.chargedEffect.effectOrDefault(1);
|
||||
}
|
||||
|
||||
if (dim) return allPow * dimPow[dim];
|
||||
// This isn't entirely accurate because you can't return a power for all ADs if only some of them actually have
|
||||
// it, so we cheat somewhat by returning the geometric mean of all actively producing dimensions (this should
|
||||
// be close to the same value if all the base multipliers are similar in magnitude)
|
||||
return allPow * Math.exp(dimPow.slice(1)
|
||||
.map(n => Math.log(n)).sum() / MultiplierTabHelper.activeDimCount("AD"));
|
||||
},
|
||||
isActive: () => PlayerProgress.infinityUnlocked() && !EternityChallenge(11).isRunning,
|
||||
icon: MultiplierTabIcons.UPGRADE("infinity"),
|
||||
},
|
||||
breakInfinityUpgrade: {
|
||||
name: "Break Infinity Upgrades",
|
||||
multValue: dim => {
|
||||
const mult = DC.D1.timesEffectsOf(
|
||||
BreakInfinityUpgrade.totalAMMult,
|
||||
BreakInfinityUpgrade.currentAMMult,
|
||||
BreakInfinityUpgrade.achievementMult,
|
||||
BreakInfinityUpgrade.slowestChallengeMult,
|
||||
BreakInfinityUpgrade.infinitiedMult
|
||||
);
|
||||
return Decimal.pow(mult, dim ? 1 : MultiplierTabHelper.activeDimCount("AD"));
|
||||
},
|
||||
isActive: () => player.break && !EternityChallenge(11).isRunning,
|
||||
icon: MultiplierTabIcons.BREAK_INFINITY,
|
||||
},
|
||||
infinityPower: {
|
||||
name: "Infinity Power",
|
||||
fakeValue: () => Currency.infinityPower.value.pow(InfinityDimensions.powerConversionRate),
|
||||
multValue: dim => {
|
||||
const mult = Currency.infinityPower.value.pow(InfinityDimensions.powerConversionRate).max(1);
|
||||
return Decimal.pow(mult, dim ? 1 : MultiplierTabHelper.activeDimCount("AD"));
|
||||
},
|
||||
isActive: () => Currency.infinityPower.value.gt(1) && !EternityChallenge(9).isRunning,
|
||||
icon: MultiplierTabIcons.INFINITY_POWER,
|
||||
},
|
||||
infinityChallenge: {
|
||||
name: dim => (dim ? `Infinity Challenges (AD ${dim})` : "Infinity Challenges"),
|
||||
multValue: dim => {
|
||||
const allMult = DC.D1.timesEffectsOf(
|
||||
InfinityChallenge(3),
|
||||
InfinityChallenge(3).reward,
|
||||
);
|
||||
|
||||
const dimMults = Array.repeat(DC.D1, 9);
|
||||
for (let tier = 1; tier <= 8; tier++) {
|
||||
dimMults[tier] = dimMults[tier].timesEffectsOf(
|
||||
tier > 1 && tier < 8 ? InfinityChallenge(8).reward : null
|
||||
);
|
||||
}
|
||||
|
||||
if (dim) return allMult.times(dimMults[dim]);
|
||||
let totalMult = DC.D1;
|
||||
for (let tier = 1; tier <= MultiplierTabHelper.activeDimCount("AD"); tier++) {
|
||||
totalMult = totalMult.times(dimMults[tier]).times(allMult);
|
||||
}
|
||||
return totalMult;
|
||||
},
|
||||
powValue: () => InfinityChallenge(4).reward.effectOrDefault(1),
|
||||
isActive: () => player.break && !EternityChallenge(11).isRunning,
|
||||
icon: MultiplierTabIcons.CHALLENGE("infinity"),
|
||||
},
|
||||
timeStudy: {
|
||||
name: dim => (dim ? `Time Studies (AD ${dim})` : "Time Studies"),
|
||||
multValue: dim => {
|
||||
const allMult = DC.D1.timesEffectsOf(
|
||||
TimeStudy(91),
|
||||
TimeStudy(101),
|
||||
TimeStudy(161),
|
||||
TimeStudy(193),
|
||||
);
|
||||
|
||||
const dimMults = Array.repeat(DC.D1, 9);
|
||||
for (let tier = 1; tier <= 8; tier++) {
|
||||
// We don't want to double-count the base effect that TS31 boosts
|
||||
const infinitiedMult = DC.D1.timesEffectsOf(
|
||||
AntimatterDimension(tier).infinityUpgrade,
|
||||
BreakInfinityUpgrade.infinitiedMult
|
||||
);
|
||||
dimMults[tier] = dimMults[tier].times(infinitiedMult.pow(TimeStudy(31).effectOrDefault(1) - 1));
|
||||
|
||||
dimMults[tier] = dimMults[tier].timesEffectsOf(
|
||||
tier < 8 ? TimeStudy(71) : null,
|
||||
tier === 8 ? TimeStudy(214) : null,
|
||||
tier === 1 ? TimeStudy(234) : null,
|
||||
);
|
||||
}
|
||||
|
||||
if (dim) return allMult.times(dimMults[dim]);
|
||||
let totalMult = DC.D1;
|
||||
for (let tier = 1; tier <= MultiplierTabHelper.activeDimCount("AD"); tier++) {
|
||||
totalMult = totalMult.times(dimMults[tier]).times(allMult);
|
||||
}
|
||||
return totalMult;
|
||||
},
|
||||
isActive: () => PlayerProgress.eternityUnlocked() && !EternityChallenge(11).isRunning,
|
||||
icon: MultiplierTabIcons.TIME_STUDY,
|
||||
},
|
||||
eternityChallenge: {
|
||||
name: "Eternity Challenges",
|
||||
multValue: dim => Decimal.pow(EternityChallenge(10).effectValue,
|
||||
dim ? 1 : MultiplierTabHelper.activeDimCount("AD")),
|
||||
isActive: () => EternityChallenge(10).isRunning,
|
||||
icon: MultiplierTabIcons.CHALLENGE("eternity"),
|
||||
},
|
||||
glyph: {
|
||||
name: "Glyph Effects",
|
||||
multValue: dim => {
|
||||
const mult = getAdjustedGlyphEffect("powermult");
|
||||
return Decimal.pow(mult, dim ? 1 : MultiplierTabHelper.activeDimCount("AD"));
|
||||
},
|
||||
powValue: () => {
|
||||
const totalPow = getAdjustedGlyphEffect("powerpow") * getAdjustedGlyphEffect("effarigdimensions");
|
||||
return totalPow * (player.dilation.active ? getAdjustedGlyphEffect("dilationpow") : 1);
|
||||
},
|
||||
isActive: () => PlayerProgress.realityUnlocked() && !EternityChallenge(11).isRunning,
|
||||
icon: MultiplierTabIcons.GENERIC_GLYPH,
|
||||
},
|
||||
v: {
|
||||
name: "V-Achievements",
|
||||
powValue: () => VUnlocks.adPow.effectOrDefault(1),
|
||||
isActive: () => PlayerProgress.realityUnlocked() && !EternityChallenge(11).isRunning,
|
||||
icon: MultiplierTabIcons.ACHIEVEMENT,
|
||||
},
|
||||
alchemy: {
|
||||
name: "Glyph Alchemy",
|
||||
multValue: dim => {
|
||||
const mult = AlchemyResource.dimensionality.effectOrDefault(1)
|
||||
.times(Currency.realityMachines.value.powEffectOf(AlchemyResource.force));
|
||||
return Decimal.pow(mult, dim ? 1 : MultiplierTabHelper.activeDimCount("AD"));
|
||||
},
|
||||
powValue: dim => {
|
||||
const basePow = AlchemyResource.power.effectOrDefault(1) * Ra.momentumValue;
|
||||
// Not entirely accurate, but returns the geometric mean of all producing dimensions (which should be close)
|
||||
let inflationPow;
|
||||
if (AlchemyResource.inflation.isUnlocked) {
|
||||
if (dim) {
|
||||
inflationPow = AntimatterDimension(dim).multiplier.gte(AlchemyResource.inflation.effectValue) ? 1.05 : 1;
|
||||
} else {
|
||||
const inflated = AntimatterDimensions.all
|
||||
.countWhere(ad => ad.isProducing && ad.multiplier.gte(AlchemyResource.inflation.effectValue));
|
||||
inflationPow = Math.pow(1.05, inflated / AntimatterDimensions.all.countWhere(ad => ad.isProducing));
|
||||
}
|
||||
}
|
||||
return basePow * inflationPow;
|
||||
},
|
||||
isActive: () => Ra.unlocks.unlockGlyphAlchemy.canBeApplied && !EternityChallenge(11).isRunning,
|
||||
icon: MultiplierTabIcons.ALCHEMY,
|
||||
},
|
||||
pelle: {
|
||||
name: "Pelle Effects",
|
||||
multValue: dim => Decimal.pow(PelleUpgrade.antimatterDimensionMult.effectOrDefault(1),
|
||||
dim ? 1 : MultiplierTabHelper.activeDimCount("AD")),
|
||||
powValue: () => PelleRifts.paradox.effectOrDefault(DC.D1).toNumber(),
|
||||
isActive: () => Pelle.isDoomed && !EternityChallenge(11).isRunning,
|
||||
icon: MultiplierTabIcons.PELLE,
|
||||
},
|
||||
iap: {
|
||||
name: "Shop Tab Purchases",
|
||||
multValue: dim => {
|
||||
const mult = ShopPurchase.dimPurchases.currentMult * ShopPurchase.allDimPurchases.currentMult;
|
||||
return Decimal.pow(mult, dim ? 1 : MultiplierTabHelper.activeDimCount("AD"));
|
||||
},
|
||||
isActive: () => ShopPurchaseData.totalSTD > 0 && !EternityChallenge(11).isRunning,
|
||||
icon: MultiplierTabIcons.IAP,
|
||||
},
|
||||
|
||||
effectNC: {
|
||||
name: dim => (dim ? `Normal Challenge Effect (AD ${dim})` : "Normal Challenge Effects"),
|
||||
multValue: dim => {
|
||||
let dimMults = Array.repeat(DC.D1, 9);
|
||||
if (NormalChallenge(2).isRunning) {
|
||||
dimMults = Array.repeat(new Decimal(player.chall2Pow), 9);
|
||||
} else if (NormalChallenge(3).isRunning) {
|
||||
dimMults[1] = new Decimal(player.chall3Pow);
|
||||
} else if (NormalChallenge(12).isRunning) {
|
||||
dimMults[2] = AntimatterDimension(2).totalAmount.pow(0.6);
|
||||
dimMults[4] = AntimatterDimension(4).totalAmount.pow(0.4);
|
||||
dimMults[6] = AntimatterDimension(6).totalAmount.pow(0.2);
|
||||
}
|
||||
|
||||
if (dim) return dimMults[dim];
|
||||
let totalMult = DC.D1;
|
||||
for (let tier = 1; tier <= MultiplierTabHelper.activeDimCount("AD"); tier++) {
|
||||
totalMult = totalMult.times(dimMults[tier]);
|
||||
}
|
||||
return totalMult;
|
||||
},
|
||||
isActive: () => [2, 3, 12].some(c => NormalChallenge(c).isRunning),
|
||||
icon: MultiplierTabIcons.CHALLENGE("infinity"),
|
||||
},
|
||||
nerfIC: {
|
||||
name: dim => (dim ? `Infinity Challenge Nerf (AD ${dim})` : "Infinity Challenge Nerf"),
|
||||
multValue: dim => {
|
||||
let dimMults = Array.repeat(DC.D1, 9);
|
||||
if (InfinityChallenge(4).isRunning) {
|
||||
for (let tier = 1; tier <= 8; tier++) {
|
||||
if (player.postC4Tier !== tier) {
|
||||
dimMults[tier] = dimMults[tier].pow(1 - InfinityChallenge(4).effectValue).reciprocal();
|
||||
}
|
||||
}
|
||||
} else if (InfinityChallenge(6).isRunning) {
|
||||
dimMults = Array.repeat(DC.D1.dividedByEffectOf(InfinityChallenge(6)), 9);
|
||||
} else if (InfinityChallenge(8).isRunning) {
|
||||
dimMults = Array.repeat(DC.D1.timesEffectsOf(InfinityChallenge(8)), 9);
|
||||
}
|
||||
|
||||
if (dim) return dimMults[dim];
|
||||
let totalMult = DC.D1;
|
||||
for (let tier = 1; tier <= MultiplierTabHelper.activeDimCount("AD"); tier++) {
|
||||
totalMult = totalMult.times(dimMults[tier]);
|
||||
}
|
||||
return totalMult;
|
||||
},
|
||||
isActive: () => [4, 6, 8].some(ic => InfinityChallenge(ic).isRunning),
|
||||
icon: MultiplierTabIcons.CHALLENGE("infinity"),
|
||||
},
|
||||
nerfV: {
|
||||
name: "V's Reality",
|
||||
powValue: () => 0.5,
|
||||
isActive: () => V.isRunning,
|
||||
icon: MultiplierTabIcons.GENERIC_V,
|
||||
},
|
||||
nerfCursed: {
|
||||
name: "Cursed Glyphs",
|
||||
powValue: () => getAdjustedGlyphEffect("curseddimensions"),
|
||||
isActive: () => getAdjustedGlyphEffect("curseddimensions") !== 1,
|
||||
icon: MultiplierTabIcons.SPECIFIC_GLYPH("cursed"),
|
||||
},
|
||||
nerfPelle: {
|
||||
name: "Doomed Nerfs",
|
||||
multValue: 0.1,
|
||||
powValue: () => (PelleStrikes.infinity.hasStrike ? 0.5 : 1),
|
||||
isActive: () => Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.PELLE,
|
||||
}
|
||||
};
|
24
javascripts/core/secret-formula/multiplier-tab/antimatter.js
Normal file
24
javascripts/core/secret-formula/multiplier-tab/antimatter.js
Normal file
@ -0,0 +1,24 @@
|
||||
import { GameDatabase } from "../game-database";
|
||||
|
||||
import { MultiplierTabIcons } from "./icons";
|
||||
|
||||
// See index.js for documentation
|
||||
GameDatabase.multiplierTabValues.AM = {
|
||||
total: {
|
||||
name: "Antimatter Production",
|
||||
displayOverride: () => `${format(Currency.antimatter.productionPerSecond, 2, 2)}/sec`,
|
||||
multValue: () => new Decimal(Currency.antimatter.productionPerSecond).clampMin(1),
|
||||
isActive: true,
|
||||
overlay: ["<i class='fas fa-atom' />"],
|
||||
},
|
||||
effarigAM: {
|
||||
name: "Glyph Effect - Effarig Antimatter Production",
|
||||
powValue: () => {
|
||||
const ad1 = AntimatterDimension(1);
|
||||
const baseProd = ad1.totalAmount.times(ad1.multiplier).times(Tickspeed.perSecond);
|
||||
return Math.pow(baseProd.log10(), getAdjustedGlyphEffect("effarigantimatter") - 1);
|
||||
},
|
||||
isActive: () => getAdjustedGlyphEffect("effarigantimatter") > 1 && AntimatterDimension(1).isProducing,
|
||||
icon: MultiplierTabIcons.SPECIFIC_GLYPH("effarig"),
|
||||
}
|
||||
};
|
@ -0,0 +1,93 @@
|
||||
import { DC } from "../../constants";
|
||||
import { GameDatabase } from "../game-database";
|
||||
import { PlayerProgress } from "../../app/player-progress";
|
||||
|
||||
import { MultiplierTabIcons } from "./icons";
|
||||
|
||||
// See index.js for documentation
|
||||
GameDatabase.multiplierTabValues.DT = {
|
||||
total: {
|
||||
name: "Dilated Time gain",
|
||||
isBase: true,
|
||||
displayOverride: () => `${format(getDilationGainPerSecond().times(getGameSpeedupForDisplay()), 2, 2)}/sec`,
|
||||
multValue: () => getDilationGainPerSecond().times(getGameSpeedupForDisplay()),
|
||||
isActive: () => getDilationGainPerSecond().gt(0),
|
||||
dilationEffect: () => (Enslaved.isRunning ? 0.85 : 1),
|
||||
isDilated: true,
|
||||
overlay: ["Ψ"],
|
||||
},
|
||||
achievement: {
|
||||
name: "Achievements",
|
||||
multValue: () => Achievement(132).effectOrDefault(1) * Achievement(137).effectOrDefault(1),
|
||||
isActive: () => Achievement(132).canBeApplied || Achievement(137).canBeApplied,
|
||||
icon: MultiplierTabIcons.ACHIEVEMENT,
|
||||
},
|
||||
dilation: {
|
||||
name: "Repeatable Dilation Upgrades",
|
||||
multValue: () => DC.D1.timesEffectsOf(
|
||||
DilationUpgrade.dtGain,
|
||||
DilationUpgrade.dtGainPelle,
|
||||
DilationUpgrade.flatDilationMult
|
||||
),
|
||||
isActive: () => DC.D1.timesEffectsOf(
|
||||
DilationUpgrade.dtGain,
|
||||
DilationUpgrade.dtGainPelle,
|
||||
DilationUpgrade.flatDilationMult
|
||||
).gt(1),
|
||||
icon: MultiplierTabIcons.UPGRADE("dilation"),
|
||||
},
|
||||
gamespeed: {
|
||||
name: "Current Game speed",
|
||||
multValue: () => getGameSpeedupForDisplay(),
|
||||
isActive: () => getGameSpeedupForDisplay() > 1,
|
||||
icon: MultiplierTabIcons.GAMESPEED,
|
||||
},
|
||||
realityUpgrade: {
|
||||
name: "Temporal Amplifier",
|
||||
multValue: () => RealityUpgrade(1).effectOrDefault(1),
|
||||
isActive: () => RealityUpgrade(1).canBeApplied && !Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.UPGRADE("reality"),
|
||||
},
|
||||
glyph: {
|
||||
name: "Glyph Effects",
|
||||
multValue: () => Decimal.times(getAdjustedGlyphEffect("dilationDT"),
|
||||
Math.clampMin(Decimal.log10(Replicanti.amount) * getAdjustedGlyphEffect("replicationdtgain"), 1))
|
||||
.times(Pelle.specialGlyphEffect.dilation),
|
||||
isActive: () => PlayerProgress.realityUnlocked(),
|
||||
icon: MultiplierTabIcons.GENERIC_GLYPH
|
||||
},
|
||||
ra: {
|
||||
name: "Ra Upgrades",
|
||||
multValue: () => DC.D1.timesEffectsOf(
|
||||
Ra.unlocks.continuousTTBoost.effects.dilatedTime,
|
||||
Ra.unlocks.peakGamespeedDT
|
||||
),
|
||||
isActive: () => Ra.unlocks.autoTP.canBeApplied,
|
||||
icon: MultiplierTabIcons.GENERIC_RA,
|
||||
},
|
||||
alchemy: {
|
||||
name: "Glyph Alchemy",
|
||||
multValue: () => AlchemyResource.dilation.effectOrDefault(1),
|
||||
isActive: () => Ra.unlocks.unlockGlyphAlchemy.canBeApplied,
|
||||
icon: MultiplierTabIcons.ALCHEMY,
|
||||
},
|
||||
iap: {
|
||||
name: "Shop Tab Purchases",
|
||||
multValue: () => new Decimal(ShopPurchase.dilatedTimePurchases.currentMult ** (Pelle.isDoomed ? 0.5 : 1)),
|
||||
isActive: () => ShopPurchaseData.totalSTD > 0,
|
||||
icon: MultiplierTabIcons.IAP,
|
||||
},
|
||||
|
||||
nerfV: {
|
||||
name: "V's Reality",
|
||||
powValue: () => 0.5,
|
||||
isActive: () => V.isRunning,
|
||||
icon: MultiplierTabIcons.GENERIC_V,
|
||||
},
|
||||
nerfPelle: {
|
||||
name: "Doomed Nerfs",
|
||||
multValue: 1e-5,
|
||||
isActive: () => Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.PELLE,
|
||||
}
|
||||
};
|
38
javascripts/core/secret-formula/multiplier-tab/eternities.js
Normal file
38
javascripts/core/secret-formula/multiplier-tab/eternities.js
Normal file
@ -0,0 +1,38 @@
|
||||
import { GameDatabase } from "../game-database";
|
||||
|
||||
import { MultiplierTabIcons } from "./icons";
|
||||
|
||||
// See index.js for documentation
|
||||
GameDatabase.multiplierTabValues.eternities = {
|
||||
total: {
|
||||
name: "Eternities gained per Eternity",
|
||||
isBase: true,
|
||||
multValue: () => gainedEternities(),
|
||||
isActive: () => (PlayerProgress.realityUnlocked() || Achievement(113).isUnlocked) && !Pelle.isDoomed,
|
||||
overlay: ["Δ", "<i class='fa-solid fa-arrows-rotate' />"],
|
||||
},
|
||||
achievement: {
|
||||
name: "Achievement 113",
|
||||
multValue: () => Achievement(113).effectOrDefault(1),
|
||||
isActive: () => Achievement(113).canBeApplied,
|
||||
icon: MultiplierTabIcons.ACHIEVEMENT,
|
||||
},
|
||||
realityUpgrades: {
|
||||
name: "Eternal Amplifier",
|
||||
multValue: () => RealityUpgrade(3).effectOrDefault(1),
|
||||
isActive: () => RealityUpgrade(3).canBeApplied,
|
||||
icon: MultiplierTabIcons.UPGRADE("reality"),
|
||||
},
|
||||
glyph: {
|
||||
name: "Equipped Glyphs",
|
||||
multValue: () => getAdjustedGlyphEffect("timeetermult"),
|
||||
isActive: () => PlayerProgress.realityUnlocked(),
|
||||
icon: MultiplierTabIcons.GENERIC_GLYPH,
|
||||
},
|
||||
alchemy: {
|
||||
name: "Eternity Alchemy Resource",
|
||||
powValue: () => AlchemyResource.eternity.effectOrDefault(1),
|
||||
isActive: () => AlchemyResource.eternity.canBeApplied,
|
||||
icon: MultiplierTabIcons.ALCHEMY,
|
||||
},
|
||||
};
|
@ -0,0 +1,100 @@
|
||||
import { DC } from "../../constants";
|
||||
import { GameDatabase } from "../game-database";
|
||||
import { PlayerProgress } from "../../app/player-progress";
|
||||
|
||||
import { MultiplierTabIcons } from "./icons";
|
||||
|
||||
// See index.js for documentation
|
||||
GameDatabase.multiplierTabValues.EP = {
|
||||
total: {
|
||||
name: "Total EP Gained on Eternity",
|
||||
isBase: true,
|
||||
multValue: () => gainedEternityPoints(),
|
||||
isActive: () => new Decimal(Currency.eternities.value).gt(0) || gainedEternityPoints().gt(0),
|
||||
dilationEffect: () => (Laitela.isRunning ? 0.75 * Effects.product(DilationUpgrade.dilationPenalty) : 1),
|
||||
isDilated: true,
|
||||
overlay: ["Δ", "<i class='fa-solid fa-layer-group' />"],
|
||||
},
|
||||
base: {
|
||||
name: "Base Eternity Points",
|
||||
isBase: true,
|
||||
fakeValue: DC.D5,
|
||||
multValue: () => DC.D5.pow(player.records.thisEternity.maxIP.plus(
|
||||
gainedInfinityPoints()).log10() / (308 - PelleRifts.recursion.effectValue.toNumber()) - 0.7),
|
||||
isActive: () => PlayerProgress.eternityUnlocked(),
|
||||
icon: MultiplierTabIcons.CONVERT_FROM("IP"),
|
||||
},
|
||||
IP: {
|
||||
name: "Eternity Points from Infinity Points",
|
||||
displayOverride: () => `${format(player.records.thisEternity.maxIP.plus(gainedInfinityPoints()), 2, 2)} IP`,
|
||||
// Just needs to match the value in base and be larger than 1
|
||||
multValue: DC.D5,
|
||||
isActive: () => PlayerProgress.eternityUnlocked(),
|
||||
icon: MultiplierTabIcons.SPECIFIC_GLYPH("infinity"),
|
||||
},
|
||||
divisor: {
|
||||
name: "Formula Improvement",
|
||||
displayOverride: () => {
|
||||
const div = 308 - PelleRifts.recursion.effectValue.toNumber();
|
||||
return `log(IP)/${formatInt(308)} ➜ log(IP)/${format(div, 2, 2)}`;
|
||||
},
|
||||
powValue: () => 308 / (308 - PelleRifts.recursion.effectValue.toNumber()),
|
||||
isActive: () => PelleRifts.recursion.canBeApplied,
|
||||
icon: MultiplierTabIcons.DIVISOR("EP"),
|
||||
},
|
||||
eternityUpgrade: {
|
||||
name: () => `Repeatable ${formatX(5)} Eternity Upgrade`,
|
||||
multValue: () => EternityUpgrade.epMult.effectOrDefault(1),
|
||||
isActive: () => PlayerProgress.eternityUnlocked() && !Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.UPGRADE("eternity"),
|
||||
},
|
||||
timeStudy: {
|
||||
name: "Time Studies",
|
||||
multValue: () => DC.D1.timesEffectsOf(
|
||||
TimeStudy(61),
|
||||
TimeStudy(121),
|
||||
TimeStudy(122),
|
||||
TimeStudy(123),
|
||||
),
|
||||
isActive: () => PlayerProgress.eternityUnlocked() && !Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.TIME_STUDY,
|
||||
},
|
||||
glyph: {
|
||||
name: "Equipped Glyphs",
|
||||
multValue: () => DC.D1.timesEffectsOf(GlyphEffect.epMult).times(Pelle.specialGlyphEffect.time),
|
||||
powValue: () => (GlyphAlteration.isAdded("time") ? getSecondaryGlyphEffect("timeEP") : 1),
|
||||
isActive: () => PlayerProgress.realityUnlocked(),
|
||||
icon: MultiplierTabIcons.GENERIC_GLYPH,
|
||||
},
|
||||
realityUpgrade: {
|
||||
name: "The Knowing Existence",
|
||||
multValue: () => RealityUpgrade(12).effectOrDefault(1),
|
||||
isActive: () => RealityUpgrade(12).canBeApplied && !Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.UPGRADE("reality"),
|
||||
},
|
||||
pelle: {
|
||||
name: "Pelle Rift Effects",
|
||||
multValue: () => PelleRifts.vacuum.milestones[2].effectOrDefault(1),
|
||||
isActive: () => PelleRifts.vacuum.milestones[2].canBeApplied,
|
||||
icon: MultiplierTabIcons.PELLE,
|
||||
},
|
||||
iap: {
|
||||
name: "Shop Tab Purchases",
|
||||
multValue: () => ShopPurchase.EPPurchases.currentMult,
|
||||
isActive: () => ShopPurchaseData.totalSTD > 0,
|
||||
icon: MultiplierTabIcons.IAP,
|
||||
},
|
||||
|
||||
nerfTeresa: {
|
||||
name: "Teresa's Reality",
|
||||
powValue: () => 0.55,
|
||||
isActive: () => Teresa.isRunning,
|
||||
icon: MultiplierTabIcons.GENERIC_TERESA,
|
||||
},
|
||||
nerfV: {
|
||||
name: "V's Reality",
|
||||
powValue: () => 0.5,
|
||||
isActive: () => V.isRunning,
|
||||
icon: MultiplierTabIcons.GENERIC_V,
|
||||
},
|
||||
};
|
60
javascripts/core/secret-formula/multiplier-tab/galaxies.js
Normal file
60
javascripts/core/secret-formula/multiplier-tab/galaxies.js
Normal file
@ -0,0 +1,60 @@
|
||||
import { GameDatabase } from "../game-database";
|
||||
|
||||
import { MultiplierTabHelper } from "./helper-functions";
|
||||
import { MultiplierTabIcons } from "./icons";
|
||||
|
||||
// See index.js for documentation
|
||||
GameDatabase.multiplierTabValues.galaxies = {
|
||||
// Note: none of the galaxy types use the global multiplier that applies to all of them within multValue, which
|
||||
// very slightly reduces performance impact and is okay because it's applied consistently
|
||||
antimatter: {
|
||||
name: "Antimatter Galaxies",
|
||||
displayOverride: () => {
|
||||
const num = player.galaxies + GalaxyGenerator.galaxies;
|
||||
const mult = MultiplierTabHelper.globalGalaxyMult();
|
||||
return `${formatInt(num)}, ${formatX(mult, 2, 2)} strength`;
|
||||
},
|
||||
multValue: () => Decimal.pow10(player.galaxies + GalaxyGenerator.galaxies),
|
||||
isActive: true,
|
||||
icon: MultiplierTabIcons.ANTIMATTER,
|
||||
},
|
||||
replicanti: {
|
||||
name: "Replicanti Galaxies",
|
||||
displayOverride: () => {
|
||||
const num = Replicanti.galaxies.total;
|
||||
let rg = Replicanti.galaxies.bought;
|
||||
rg *= (1 + Effects.sum(TimeStudy(132), TimeStudy(133)));
|
||||
rg += Replicanti.galaxies.extra;
|
||||
rg += Math.min(Replicanti.galaxies.bought, ReplicantiUpgrade.galaxies.value) *
|
||||
Effects.sum(EternityChallenge(8).reward);
|
||||
const mult = rg / Math.clampMin(num, 1) * MultiplierTabHelper.globalGalaxyMult();
|
||||
return `${formatInt(num)}, ${formatX(mult, 2, 2)} strength`;
|
||||
},
|
||||
multValue: () => {
|
||||
let rg = Replicanti.galaxies.bought;
|
||||
rg *= (1 + Effects.sum(TimeStudy(132), TimeStudy(133)));
|
||||
rg += Replicanti.galaxies.extra;
|
||||
rg += Math.min(Replicanti.galaxies.bought, ReplicantiUpgrade.galaxies.value) *
|
||||
Effects.sum(EternityChallenge(8).reward);
|
||||
return Decimal.pow10(rg);
|
||||
},
|
||||
isActive: () => Replicanti.areUnlocked,
|
||||
icon: MultiplierTabIcons.SPECIFIC_GLYPH("replication"),
|
||||
},
|
||||
tachyon: {
|
||||
name: "Tachyon Galaxies",
|
||||
displayOverride: () => {
|
||||
const num = player.dilation.totalTachyonGalaxies;
|
||||
const mult = MultiplierTabHelper.globalGalaxyMult() *
|
||||
(1 + Math.max(0, Replicanti.amount.log10() / 1e6) * AlchemyResource.alternation.effectValue);
|
||||
return `${formatInt(num)}, ${formatX(mult, 2, 2)} strength`;
|
||||
},
|
||||
multValue: () => {
|
||||
const num = player.dilation.totalTachyonGalaxies;
|
||||
const mult = 1 + Math.max(0, Replicanti.amount.log10() / 1e6) * AlchemyResource.alternation.effectValue;
|
||||
return Decimal.pow10(num * mult);
|
||||
},
|
||||
isActive: () => player.dilation.totalTachyonGalaxies > 0,
|
||||
icon: MultiplierTabIcons.SPECIFIC_GLYPH("dilation"),
|
||||
},
|
||||
};
|
103
javascripts/core/secret-formula/multiplier-tab/gamespeed.js
Normal file
103
javascripts/core/secret-formula/multiplier-tab/gamespeed.js
Normal file
@ -0,0 +1,103 @@
|
||||
import { GameDatabase } from "../game-database";
|
||||
|
||||
import { MultiplierTabHelper } from "./helper-functions";
|
||||
import { MultiplierTabIcons } from "./icons";
|
||||
|
||||
// See index.js for documentation
|
||||
GameDatabase.multiplierTabValues.gamespeed = {
|
||||
total: {
|
||||
name: "Game speed",
|
||||
isBase: true,
|
||||
displayOverride: () => {
|
||||
if (Enslaved.isStoringRealTime) return `Set to ${format(0)} (storing real time)`;
|
||||
if (EternityChallenge(12).isRunning) return `${formatX(1)}/${formatInt(1000)} (fixed)`;
|
||||
const curr = getGameSpeedupFactor();
|
||||
|
||||
const bh = MultiplierTabHelper.blackHoleSpeeds();
|
||||
const currBH = bh.current;
|
||||
const avgBH = bh.average;
|
||||
|
||||
const avgSpeed = Enslaved.isAutoReleasing
|
||||
? getGameSpeedupForDisplay()
|
||||
: curr / currBH * avgBH;
|
||||
const avgString = ` (current) | ${formatX(avgSpeed, 2, 2)} (average)`;
|
||||
return `${formatX(curr, 2, 2)}${curr === avgSpeed ? "" : avgString}`;
|
||||
},
|
||||
multValue: () => getGameSpeedupForDisplay(),
|
||||
isActive: () => PlayerProgress.seenAlteredSpeed(),
|
||||
dilationEffect: () => (Effarig.isRunning ? Effarig.multDilation : 1),
|
||||
isDilated: true,
|
||||
overlay: ["Δ", `<i class="fas fa-clock" />`, `<i class="fas fa-circle" />`],
|
||||
},
|
||||
glyph: {
|
||||
name: "Equipped Glyphs",
|
||||
multValue: () => getAdjustedGlyphEffect("timespeed"),
|
||||
powValue: () => getAdjustedGlyphEffect("effarigblackhole"),
|
||||
isActive: () => PlayerProgress.realityUnlocked() && !EternityChallenge(12).isRunning,
|
||||
icon: MultiplierTabIcons.GENERIC_GLYPH,
|
||||
},
|
||||
blackHoleCurr: {
|
||||
name: "Current Black Hole Speedup",
|
||||
multValue: () => MultiplierTabHelper.blackHoleSpeeds().current,
|
||||
isActive: () => BlackHole(1).isUnlocked && !BlackHoles.arePaused && !EternityChallenge(12).isRunning,
|
||||
icon: MultiplierTabIcons.BLACK_HOLE,
|
||||
},
|
||||
blackHoleAvg: {
|
||||
name: "Average Black Hole Speedup",
|
||||
multValue: () => MultiplierTabHelper.blackHoleSpeeds().average,
|
||||
isActive: () => BlackHole(1).isUnlocked && !BlackHoles.arePaused && !EternityChallenge(12).isRunning,
|
||||
icon: MultiplierTabIcons.BLACK_HOLE,
|
||||
},
|
||||
achievementMult: {
|
||||
name: "Achievement Multiplier",
|
||||
multValue: () => Math.pow(VUnlocks.achievementBH.effectOrDefault(1),
|
||||
BlackHoles.list.countWhere(bh => bh.isUnlocked)),
|
||||
isActive: () => !BlackHoles.arePaused && VUnlocks.achievementBH.canBeApplied && !EternityChallenge(12).isRunning,
|
||||
icon: MultiplierTabIcons.ACHIEVEMENT,
|
||||
},
|
||||
pulsing: {
|
||||
name: "Auto-releasing Stored Time",
|
||||
multValue: () => (Enslaved.isAutoReleasing
|
||||
? Math.max(Enslaved.autoReleaseSpeed / getGameSpeedupFactor(), 1)
|
||||
: getGameSpeedupFactor()),
|
||||
isActive: () => Enslaved.canRelease() && Enslaved.isAutoReleasing && !EternityChallenge(12).isRunning,
|
||||
icon: MultiplierTabIcons.BH_PULSE,
|
||||
},
|
||||
singularity: {
|
||||
name: "Singularity Milestones",
|
||||
multValue: () => SingularityMilestone.gamespeedFromSingularities.effectOrDefault(1),
|
||||
isActive: () => SingularityMilestone.gamespeedFromSingularities.canBeApplied && !EternityChallenge(12).isRunning,
|
||||
icon: MultiplierTabIcons.SINGULARITY,
|
||||
},
|
||||
pelle: {
|
||||
name: "Pelle game speed Upgrade",
|
||||
multValue: () => PelleUpgrade.timeSpeedMult.effectValue.toNumber(),
|
||||
isActive: () => Pelle.isDoomed && !EternityChallenge(12).isRunning,
|
||||
icon: MultiplierTabIcons.PELLE,
|
||||
},
|
||||
|
||||
ec12: {
|
||||
name: "Eternity Challenge 12",
|
||||
multValue: () => 0.001 / getGameSpeedupForDisplay(),
|
||||
isActive: () => EternityChallenge(12).isRunning,
|
||||
icon: MultiplierTabIcons.CHALLENGE("eternity"),
|
||||
},
|
||||
chargingBH: {
|
||||
name: "Black Hole Charging",
|
||||
multValue: () => 1 - player.celestials.enslaved.storedFraction,
|
||||
isActive: () => Enslaved.isStoringGameTime,
|
||||
icon: MultiplierTabIcons.BLACK_HOLE,
|
||||
},
|
||||
invertedBH: {
|
||||
name: "Inverted Black Hole",
|
||||
multValue: () => player.blackHoleNegative,
|
||||
isActive: () => BlackHoles.areNegative,
|
||||
icon: MultiplierTabIcons.CHALLENGE("eternity"),
|
||||
},
|
||||
nerfLaitela: {
|
||||
name: "Lai'tela's Reality",
|
||||
powValue: () => Math.clampMax(Time.thisRealityRealTime.totalMinutes / 10, 1),
|
||||
isActive: () => Laitela.isRunning,
|
||||
icon: MultiplierTabIcons.GENERIC_LAITELA,
|
||||
}
|
||||
};
|
130
javascripts/core/secret-formula/multiplier-tab/general.js
Normal file
130
javascripts/core/secret-formula/multiplier-tab/general.js
Normal file
@ -0,0 +1,130 @@
|
||||
import { DC } from "../../constants";
|
||||
import { GameDatabase } from "../game-database";
|
||||
|
||||
import { MultiplierTabHelper } from "./helper-functions";
|
||||
import { MultiplierTabIcons } from "./icons";
|
||||
|
||||
// See index.js for documentation
|
||||
GameDatabase.multiplierTabValues.general = {
|
||||
achievement: {
|
||||
name: (ach, dim) => (dim?.length === 2
|
||||
? `Achievement ${ach} (${dim})`
|
||||
: `Achievement ${ach}`),
|
||||
multValue: (ach, dim) => {
|
||||
// There is also a buy10 effect, but we don't track that in the multiplier tab
|
||||
if (ach === 141) return Achievement(141).canBeApplied ? Achievement(141).effects.ipGain.effectOrDefault(1) : 1;
|
||||
if (!dim) return Achievement(ach).canBeApplied ? Achievement(ach).effectOrDefault(1) : 1;
|
||||
|
||||
if (dim?.length === 2) {
|
||||
let totalEffect = DC.D1;
|
||||
for (let tier = 1; tier <= MultiplierTabHelper.activeDimCount(dim); tier++) {
|
||||
let singleEffect;
|
||||
if (ach === 43) singleEffect = Achievement(43).canBeApplied ? (1 + tier / 100) : 1;
|
||||
else singleEffect = (MultiplierTabHelper.achievementDimCheck(ach, `${dim}${tier}`) &&
|
||||
Achievement(ach).canBeApplied) ? Achievement(ach).effectOrDefault(1) : 1;
|
||||
totalEffect = totalEffect.times(singleEffect);
|
||||
}
|
||||
return totalEffect;
|
||||
}
|
||||
|
||||
if (ach === 43) return Achievement(43).canBeApplied ? (1 + Number(dim.charAt(2)) / 100) : 1;
|
||||
return (MultiplierTabHelper.achievementDimCheck(ach, dim) && Achievement(ach).canBeApplied)
|
||||
? Achievement(ach).effectOrDefault(1) : 1;
|
||||
},
|
||||
isActive: ach => Achievement(ach).canBeApplied,
|
||||
icon: ach => {
|
||||
const base = MultiplierTabIcons.ACHIEVEMENT;
|
||||
return {
|
||||
color: base.color,
|
||||
symbol: `${base.symbol}${ach}`,
|
||||
};
|
||||
},
|
||||
},
|
||||
timeStudy: {
|
||||
name: (ts, dim) => (dim?.length === 2
|
||||
? `Time Study ${ts} (${dim})`
|
||||
: `Time Study ${ts}`),
|
||||
multValue: (ts, dim) => {
|
||||
if (!dim) return TimeStudy(ts).canBeApplied ? TimeStudy(ts).effectOrDefault(1) : 1;
|
||||
if (dim?.length === 2) {
|
||||
let totalEffect = DC.D1;
|
||||
for (let tier = 1; tier <= MultiplierTabHelper.activeDimCount(dim); tier++) {
|
||||
totalEffect = totalEffect.times((MultiplierTabHelper.timeStudyDimCheck(ts, `${dim}${tier}`) &&
|
||||
TimeStudy(ts).isBought) ? TimeStudy(ts).effectOrDefault(1) : 1);
|
||||
}
|
||||
return totalEffect;
|
||||
}
|
||||
// The new Decimal() wrapper is necessary because, for some inexplicable reason, replicanti becomes
|
||||
// reactive through TS101 if that isn't there
|
||||
return (MultiplierTabHelper.timeStudyDimCheck(ts, dim) && TimeStudy(ts).isBought)
|
||||
? new Decimal(TimeStudy(ts).effectOrDefault(1)) : 1;
|
||||
},
|
||||
isActive: ts => TimeStudy(ts).isBought,
|
||||
icon: ts => {
|
||||
const base = MultiplierTabIcons.TIME_STUDY;
|
||||
return {
|
||||
color: base.color,
|
||||
symbol: `${base.symbol}${ts}`,
|
||||
};
|
||||
},
|
||||
},
|
||||
infinityChallenge: {
|
||||
name: ic => `Infinity Challenge ${ic}`,
|
||||
displayOverride: ic => (ic === 4 ? formatPow(InfinityChallenge(4).reward.effectValue, 0, 3) : ""),
|
||||
multValue: (ic, dim) => {
|
||||
// We cheat here by actually giving IC4 a multiplier of a value equal to its effect on the final
|
||||
// value in order to represent its proportion accurately. It's hidden by displayOverride
|
||||
if (ic === 4) {
|
||||
const ic4Pow = InfinityChallenge(4).reward.effectValue;
|
||||
const mults = AntimatterDimensions.all.map(ad => ad.multiplier.pow((ic4Pow - 1) / ic4Pow));
|
||||
if (dim?.length === 2) return mults.reduce((x, y) => x.times(y), DC.D1);
|
||||
return mults[Number(dim.charAt(2)) - 1];
|
||||
}
|
||||
|
||||
if (dim?.length === 2) {
|
||||
let totalEffect = DC.D1;
|
||||
for (let tier = 1; tier <= MultiplierTabHelper.activeDimCount(dim); tier++) {
|
||||
totalEffect = totalEffect.times((MultiplierTabHelper.ICDimCheck(ic, `${dim}${tier}`) &&
|
||||
InfinityChallenge(ic).isCompleted) ? InfinityChallenge(ic).reward.effectOrDefault(1) : 1);
|
||||
}
|
||||
return totalEffect;
|
||||
}
|
||||
const num = Number(dim.charAt(2));
|
||||
if (ic === 8) return (num > 1 && num < 8) ? InfinityChallenge(ic).reward.effectValue : DC.D1;
|
||||
return InfinityChallenge(ic).reward.effectValue;
|
||||
},
|
||||
isActive: ic => InfinityChallenge(ic).isCompleted,
|
||||
icon: ic => {
|
||||
const base = MultiplierTabIcons.CHALLENGE("infinity");
|
||||
return {
|
||||
color: base.color,
|
||||
symbol: `${base.symbol}${ic}`,
|
||||
};
|
||||
},
|
||||
},
|
||||
eternityChallenge: {
|
||||
name: ec => `Eternity Challenge ${ec}`,
|
||||
multValue: (ec, dim) => {
|
||||
if (dim?.length === 2) {
|
||||
let totalEffect = DC.D1;
|
||||
for (let tier = 1; tier <= MultiplierTabHelper.activeDimCount(dim); tier++) {
|
||||
totalEffect = totalEffect.times(
|
||||
(MultiplierTabHelper.ECDimCheck(ec, `${dim}${tier}`) && EternityChallenge(ec).reward.canBeApplied)
|
||||
? EternityChallenge(ec).reward.effectOrDefault(1).clampMin(1)
|
||||
: 1);
|
||||
}
|
||||
return totalEffect;
|
||||
}
|
||||
if (ec === 2) return dim === "ID1" ? EternityChallenge(ec).reward.effectValue : DC.D1;
|
||||
return EternityChallenge(ec).reward.effectOrDefault(1);
|
||||
},
|
||||
isActive: ec => EternityChallenge(ec).reward.canBeApplied,
|
||||
icon: ec => {
|
||||
const base = MultiplierTabIcons.CHALLENGE("eternity");
|
||||
return {
|
||||
color: base.color,
|
||||
symbol: `${base.symbol}${ec}`,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
@ -0,0 +1,206 @@
|
||||
import { DC } from "../../../core/constants";
|
||||
|
||||
export const MultiplierTabHelper = {
|
||||
// Helper method for counting enabled dimensions
|
||||
activeDimCount(type) {
|
||||
switch (type) {
|
||||
case "AD":
|
||||
// Technically not 100% correct, but within EC7 any AD8 production is going to be irrelevant compared to AD7
|
||||
// and making the UI behave as if it's inactive produces a better look overall
|
||||
return Math.clamp(AntimatterDimensions.all.filter(ad => ad.isProducing).length,
|
||||
1, EternityChallenge(7).isRunning ? 7 : 8);
|
||||
case "ID":
|
||||
return InfinityDimensions.all.filter(id => id.isProducing).length;
|
||||
case "TD":
|
||||
return TimeDimensions.all.filter(td => td.isProducing).length;
|
||||
default:
|
||||
throw new Error("Unrecognized Dimension type in Multiplier tab GameDB entry");
|
||||
}
|
||||
},
|
||||
|
||||
// Helper method for galaxy strength multipliers affecting all galaxy types (this is used a large number of times)
|
||||
globalGalaxyMult() {
|
||||
return Effects.product(
|
||||
InfinityUpgrade.galaxyBoost,
|
||||
InfinityUpgrade.galaxyBoost.chargedEffect,
|
||||
BreakInfinityUpgrade.galaxyBoost,
|
||||
TimeStudy(212),
|
||||
TimeStudy(232),
|
||||
Achievement(86),
|
||||
Achievement(178),
|
||||
InfinityChallenge(5).reward,
|
||||
PelleUpgrade.galaxyPower,
|
||||
PelleRifts.decay.milestones[1]
|
||||
);
|
||||
},
|
||||
|
||||
// Helper method for galaxies and tickspeed, broken up as contributions of tickspeed*log(perGalaxy) and galaxyCount to
|
||||
// their product, which is proportional to log(tickspeed)
|
||||
decomposeTickspeed() {
|
||||
let effectiveCount = effectiveBaseGalaxies();
|
||||
const effects = this.globalGalaxyMult();
|
||||
|
||||
let galFrac, tickFrac;
|
||||
if (effectiveCount < 3) {
|
||||
let baseMult = 1.1245;
|
||||
if (player.galaxies === 1) baseMult = 1.11888888;
|
||||
if (player.galaxies === 2) baseMult = 1.11267177;
|
||||
if (NormalChallenge(5).isRunning) {
|
||||
baseMult = 1.08;
|
||||
if (player.galaxies === 1) baseMult = 1.07632;
|
||||
if (player.galaxies === 2) baseMult = 1.072;
|
||||
}
|
||||
// This is needed for numerical consistency with the other conditional case
|
||||
baseMult /= 0.965 ** 2;
|
||||
const logBase = Math.log10(baseMult);
|
||||
|
||||
const perGalaxy = 0.02 * effects;
|
||||
effectiveCount *= Pelle.specialGlyphEffect.power;
|
||||
|
||||
tickFrac = Tickspeed.totalUpgrades * logBase;
|
||||
galFrac = -Math.log10(Math.max(0.01, 1 / baseMult - (effectiveCount * perGalaxy))) / logBase;
|
||||
} else {
|
||||
effectiveCount -= 2;
|
||||
effectiveCount *= effects;
|
||||
effectiveCount *= getAdjustedGlyphEffect("realitygalaxies") * (1 + ImaginaryUpgrade(9).effectOrDefault(0));
|
||||
effectiveCount *= Pelle.specialGlyphEffect.power;
|
||||
|
||||
// These all need to be framed as INCREASING x/sec tick rate (ie. all multipliers > 1, all logs > 0)
|
||||
const baseMult = 0.965 ** 2 / (NormalChallenge(5).isRunning ? 0.83 : 0.8);
|
||||
const logBase = Math.log10(baseMult);
|
||||
const logPerGalaxy = -DC.D0_965.log10();
|
||||
|
||||
tickFrac = Tickspeed.totalUpgrades * logBase;
|
||||
galFrac = (1 + effectiveCount / logBase * logPerGalaxy);
|
||||
}
|
||||
|
||||
// Artificially inflate the galaxy portion in order to make the breakdown closer to 50/50 in common situations
|
||||
galFrac *= 3;
|
||||
|
||||
// Calculate what proportion base tickspeed takes out of the entire tickspeed multiplier
|
||||
const base = DC.D1.dividedByEffectsOf(
|
||||
Achievement(36),
|
||||
Achievement(45),
|
||||
Achievement(66),
|
||||
Achievement(83)
|
||||
);
|
||||
let baseFrac = base.log10() / Tickspeed.perSecond.log10();
|
||||
|
||||
// We want to make sure to zero out components in some edge cases
|
||||
if (base.eq(1)) baseFrac = 0;
|
||||
if (effectiveCount === 0) galFrac = 0;
|
||||
|
||||
// Normalize the sum by splitting tickspeed and galaxies across what's leftover besides the base value. These three
|
||||
// values must be scaled so that they sum to 1 and none are negative
|
||||
let factor = (1 - baseFrac) / (tickFrac + galFrac);
|
||||
// The actual base tickspeed calculation multiplies things in a different order, which can lead to precision issues
|
||||
// when no tickspeed upgrades have been bought if we don't explicitly set this to zero
|
||||
if (Tickspeed.totalUpgrades === 0) factor = 0;
|
||||
return {
|
||||
base: baseFrac,
|
||||
tickspeed: tickFrac * factor,
|
||||
galaxies: galFrac * factor,
|
||||
};
|
||||
},
|
||||
|
||||
// Helper method to check for whether an achievement affects a particular dimension or not. Format of dimStr is
|
||||
// expected to be a three-character string "XXN", eg. "AD3" or "TD2"
|
||||
achievementDimCheck(ach, dimStr) {
|
||||
switch (ach) {
|
||||
case 23:
|
||||
return dimStr === "AD8";
|
||||
case 28:
|
||||
case 31:
|
||||
case 68:
|
||||
case 71:
|
||||
return dimStr === "AD1";
|
||||
case 94:
|
||||
return dimStr === "ID1";
|
||||
case 34:
|
||||
return dimStr.substr(0, 2) === "AD" && Number(dimStr.charAt(2)) !== 8;
|
||||
case 64:
|
||||
return dimStr.substr(0, 2) === "AD" && Number(dimStr.charAt(2)) <= 4;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
// Helper method to check for whether a time study affects a particular dimension or not, see achievementDimCheck()
|
||||
timeStudyDimCheck(ts, dimStr) {
|
||||
switch (ts) {
|
||||
case 11:
|
||||
return dimStr === "TD1";
|
||||
case 71:
|
||||
return dimStr.substr(0, 2) === "AD" && Number(dimStr.charAt(2)) !== 8;
|
||||
case 72:
|
||||
return dimStr === "ID4";
|
||||
case 73:
|
||||
return dimStr === "TD3";
|
||||
case 214:
|
||||
return dimStr === "AD8";
|
||||
case 227:
|
||||
return dimStr === "TD4";
|
||||
case 234:
|
||||
return dimStr === "AD1";
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
// Helper method to check for whether an IC reward affects a particular dimension or not, see achievementDimCheck()
|
||||
ICDimCheck(ic, dimStr) {
|
||||
switch (ic) {
|
||||
case 1:
|
||||
case 6:
|
||||
return dimStr.substr(0, 2) === "ID";
|
||||
case 3:
|
||||
case 4:
|
||||
return dimStr.substr(0, 2) === "AD";
|
||||
case 8:
|
||||
return dimStr.substr(0, 2) === "AD" && Number(dimStr.charAt(2)) > 1 && Number(dimStr.charAt(2)) < 8;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// Helper method to check for whether an EC reward affects a particular dimension or not, see achievementDimCheck()
|
||||
ECDimCheck(ec, dimStr) {
|
||||
switch (ec) {
|
||||
case 1:
|
||||
case 10:
|
||||
return dimStr.substr(0, 2) === "TD";
|
||||
case 2:
|
||||
return dimStr === "ID1";
|
||||
case 4:
|
||||
case 9:
|
||||
return dimStr.substr(0, 2) === "ID";
|
||||
case 7:
|
||||
return dimStr === "ID8";
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
blackHoleSpeeds() {
|
||||
const currBH = BlackHoles.list
|
||||
.filter(bh => bh.isUnlocked)
|
||||
.map(bh => (bh.isActive ? bh.power : 1))
|
||||
.reduce((x, y) => x * y, 1);
|
||||
|
||||
// Calculate an average black hole speedup factor
|
||||
const bh1 = BlackHole(1);
|
||||
const bh2 = BlackHole(2);
|
||||
const avgBH = 1 + (bh1.isUnlocked ? bh1.dutyCycle * (bh1.power - 1) : 0) +
|
||||
(bh2.isUnlocked ? bh1.dutyCycle * bh2.dutyCycle * bh1.power * (bh2.power - 1) : 0);
|
||||
|
||||
return {
|
||||
current: currBH,
|
||||
average: avgBH
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// All the resource files in this GameDB folder set props of multiplierTabValues, but it needs to be initialized.
|
||||
// This file comes first in the import order and thus will make sure that nothing else attempts to define a prop
|
||||
// on an undefined object
|
||||
GameDatabase.multiplierTabValues = {};
|
218
javascripts/core/secret-formula/multiplier-tab/icons.js
Normal file
218
javascripts/core/secret-formula/multiplier-tab/icons.js
Normal file
@ -0,0 +1,218 @@
|
||||
/**
|
||||
* Every entry in this object is a styling specification for bars within the multiplier tab.
|
||||
* {
|
||||
* @property {String} text String specifying the color to render the background of the bar (often a CSS var)
|
||||
* @property {String} symbol String to show as text on the bar, may be HTML (allows for font awesome icons)
|
||||
* @property {String} textColor A text color to override the default --color-text for better contrast
|
||||
* }
|
||||
*/
|
||||
export const MultiplierTabIcons = {
|
||||
DIMENSION(type, tier) {
|
||||
const tierText = tier ?? "";
|
||||
switch (type) {
|
||||
case "AD":
|
||||
return { symbol: `<b>Ω${tierText}</b>`, color: "var(--color-antimatter)" };
|
||||
case "ID":
|
||||
return { symbol: `<b>∞${tierText}</b>`, color: "var(--color-infinity)" };
|
||||
case "TD":
|
||||
return { symbol: `<b>Δ${tierText}</b>`, color: "var(--color-eternity)" };
|
||||
default:
|
||||
throw new Error("Unrecognized dimension type in multiplier tab icons");
|
||||
}
|
||||
},
|
||||
PURCHASE(type, tier) {
|
||||
const symbol = `<i class="fas fa-arrow-up-right-dots" />${tier ?? ""}`;
|
||||
switch (type) {
|
||||
case "AD":
|
||||
return { symbol, color: "var(--color-antimatter)" };
|
||||
case "ID":
|
||||
return { symbol, color: "var(--color-infinity)" };
|
||||
case "TD":
|
||||
return { symbol, color: "var(--color-eternity)" };
|
||||
case "baseID":
|
||||
return { symbol: `<i class="fas fa-arrows-up-to-line" />`, color: "var(--color-infinity)" };
|
||||
case "tesseractID":
|
||||
return {
|
||||
symbol: `<i class="fas fa-up-right-and-down-left-from-center" />`,
|
||||
color: "var(--color-enslaved--base)"
|
||||
};
|
||||
default:
|
||||
throw new Error("Unrecognized purchase type in multiplier tab icons");
|
||||
}
|
||||
},
|
||||
CHALLENGE(type, tier) {
|
||||
const tierText = `<i class="fas fa-arrow-down-wide-short" />${tier ?? ""}`;
|
||||
switch (type) {
|
||||
case "infinity":
|
||||
return { symbol: `<b>∞</b>${tierText}`, color: "var(--color-infinity)" };
|
||||
case "eternity":
|
||||
return { symbol: `<b>Δ</b>${tierText}`, color: "var(--color-eternity)" };
|
||||
default:
|
||||
throw new Error("Unrecognized challenge type in multiplier tab icons");
|
||||
}
|
||||
},
|
||||
// Regular sacrifice and glyph sacrifice
|
||||
SACRIFICE(type) {
|
||||
const icon = `<i class="fas fa-turn-down" />`;
|
||||
switch (type) {
|
||||
case "antimatter":
|
||||
return { symbol: `<b>Ω</b>${icon}`, color: "var(--color-antimatter)" };
|
||||
case "infinity":
|
||||
return { symbol: `<b>∞</b>${icon}`, color: "var(--color-infinity)" };
|
||||
case "time":
|
||||
return { symbol: `<b>Δ</b>${icon}`, color: "var(--color-eternity)" };
|
||||
case "dilation":
|
||||
return { symbol: `<b>Ψ</b>${icon}`, color: "var(--color-dilation)", textColor: "black" };
|
||||
default:
|
||||
throw new Error("Unrecognized sacrifice type in multiplier tab icons");
|
||||
}
|
||||
},
|
||||
UPGRADE(type) {
|
||||
const icon = `<i class="fas fa-arrow-up" />`;
|
||||
switch (type) {
|
||||
case "infinity":
|
||||
return { symbol: `<b>∞</b>${icon}`, color: "var(--color-infinity)" };
|
||||
case "eternity":
|
||||
return { symbol: `<b>Δ</b>${icon}`, color: "var(--color-eternity)" };
|
||||
case "dilation":
|
||||
return { symbol: `<b>Ψ</b>${icon}`, color: "var(--color-dilation)" };
|
||||
case "reality":
|
||||
return { symbol: `<b>Ϟ</b>${icon}`, color: "var(--color-reality)" };
|
||||
case "imaginary":
|
||||
return { symbol: `<i class="far fa-lightbulb" />${icon}`, color: "var(--color-ra--base)" };
|
||||
default:
|
||||
throw new Error("Unrecognized upgrade type in multiplier tab icons");
|
||||
}
|
||||
},
|
||||
// Icons for base IP/EP
|
||||
CONVERT_FROM(currency) {
|
||||
if (currency === "AM") {
|
||||
return {
|
||||
symbol: `<i class='fas fa-atom' /><i class='fa-solid fa-arrow-right-arrow-left' />`,
|
||||
color: "var(--color-antimatter)",
|
||||
};
|
||||
}
|
||||
if (currency === "IP") {
|
||||
return {
|
||||
symbol: `<b>∞</b><i class='fa-solid fa-arrow-right-arrow-left' />`,
|
||||
color: "var(--color-infinity)",
|
||||
};
|
||||
}
|
||||
return {};
|
||||
},
|
||||
// IP and EP formula divisors
|
||||
DIVISOR(currency) {
|
||||
let color;
|
||||
if (currency === "IP") color = "var(--color-infinity)";
|
||||
if (currency === "EP") color = "var(--color-eternity)";
|
||||
return {
|
||||
symbol: `<i class='fas fa-calculator' />`,
|
||||
color,
|
||||
};
|
||||
},
|
||||
ANTIMATTER: {
|
||||
symbol: `<i class='fas fa-atom' />`,
|
||||
color: "var(--color-antimatter)",
|
||||
},
|
||||
DIMBOOST: {
|
||||
symbol: `<i class="fas fa-angles-up" />`,
|
||||
color: GameDatabase.reality.glyphTypes.power.color,
|
||||
},
|
||||
TICKSPEED: {
|
||||
symbol: `<i class="fas fa-clock" />`,
|
||||
color: "var(--color-eternity)",
|
||||
},
|
||||
GALAXY: {
|
||||
symbol: `<i class="fas fa-bahai" />`,
|
||||
color: "var(--color-eternity)",
|
||||
},
|
||||
ACHIEVEMENT: {
|
||||
symbol: `<i class="fas fa-trophy" />`,
|
||||
color: "var(--color-v--base)",
|
||||
textColor: "black",
|
||||
},
|
||||
BREAK_INFINITY: {
|
||||
symbol: `<i class="fab fa-skyatlas" />`,
|
||||
color: "var(--color-infinity)",
|
||||
textColor: "black",
|
||||
},
|
||||
INFINITY_POWER: {
|
||||
symbol: `<b>∞</b><i class="fas fa-arrows-turn-right" />`,
|
||||
color: "var(--color-infinity)",
|
||||
textColor: "black",
|
||||
},
|
||||
IPOW_CONVERSION: {
|
||||
symbol: `<i class="fas fa-arrow-down-up-across-line" />`,
|
||||
color: "var(--color-infinity)",
|
||||
textColor: "black",
|
||||
},
|
||||
TIME_STUDY: {
|
||||
symbol: `<i class="fas fa-book" />`,
|
||||
color: "var(--color-eternity)",
|
||||
},
|
||||
TACHYON_PARTICLES: {
|
||||
symbol: `<i class="fas fa-meteor" />`,
|
||||
color: "var(--color-dilation)",
|
||||
},
|
||||
GENERIC_GLYPH: {
|
||||
symbol: `<i class="fas fa-clone" />`,
|
||||
color: "var(--color-reality)",
|
||||
},
|
||||
SPECIFIC_GLYPH(type) {
|
||||
return {
|
||||
symbol: `<b>${GameDatabase.reality.glyphTypes[type].symbol}</b>`,
|
||||
color: GameDatabase.reality.glyphTypes[type].color,
|
||||
};
|
||||
},
|
||||
BLACK_HOLE: {
|
||||
symbol: `<i class="fas fa-circle" />`,
|
||||
color: "var(--color-reality)",
|
||||
},
|
||||
GAMESPEED: {
|
||||
symbol: `<i class="fas fa-clock" />`,
|
||||
color: "var(--color-reality)",
|
||||
},
|
||||
GENERIC_TERESA: {
|
||||
symbol: "<b>Ϟ</b>",
|
||||
color: "var(--color-teresa--base)",
|
||||
},
|
||||
GENERIC_ENSLAVED: {
|
||||
symbol: `<div class="o-tab-btn--cel3">\uf0c1</div>`,
|
||||
color: "var(--color-enslaved--base)",
|
||||
},
|
||||
GENERIC_V: {
|
||||
symbol: "<b>⌬</b>",
|
||||
color: "var(--color-v--base)",
|
||||
textColor: "black",
|
||||
},
|
||||
GENERIC_RA: {
|
||||
symbol: `<i class="fas fa-sun" />`,
|
||||
color: "var(--color-ra--base)",
|
||||
},
|
||||
ALCHEMY: {
|
||||
symbol: `<i class="fas fa-vial" />`,
|
||||
color: "var(--color-ra-pet--effarig)",
|
||||
},
|
||||
BH_PULSE: {
|
||||
symbol: `<i class="fas fa-expand-arrows-alt" />`,
|
||||
color: "var(--color-reality)",
|
||||
},
|
||||
GENERIC_LAITELA: {
|
||||
symbol: "<b>ᛝ</b>",
|
||||
color: "var(--color-laitela--base)",
|
||||
textColor: "var(--color-laitela--accent)",
|
||||
},
|
||||
SINGULARITY: {
|
||||
symbol: `<i class="fas fa-arrows-up-down-left-right" />`,
|
||||
color: "var(--color-laitela--base)",
|
||||
textColor: "var(--color-laitela--accent)",
|
||||
},
|
||||
PELLE: {
|
||||
symbol: "<b>♅</b>",
|
||||
color: "var(--color-pelle--base)",
|
||||
},
|
||||
IAP: {
|
||||
symbol: `<i class="fas fa-coins" />`,
|
||||
color: "var(--color-accent)",
|
||||
},
|
||||
};
|
48
javascripts/core/secret-formula/multiplier-tab/index.js
Normal file
48
javascripts/core/secret-formula/multiplier-tab/index.js
Normal file
@ -0,0 +1,48 @@
|
||||
|
||||
import "./helper-functions";
|
||||
|
||||
/**
|
||||
* Most of the GameDB entries in this folder follow largely the same structure, but have been split into multiple
|
||||
* for purposes of organization and ease-of-use. All fields may also be functions which may or may not accept an input
|
||||
* argument, often having differing behavior based on its presence or absence - more often than not, the lack of an
|
||||
* input argument will instead calculate the total contribution (eg. not providing a dimension tier will calculate
|
||||
* the total across all dimensions).
|
||||
* {
|
||||
* @property {String} name Name to associate with this multiplier/effect
|
||||
* @property {String} isBase Suppresses the leading × in multipliers if true. Primarily
|
||||
* exists in order to avoid copy-pasting extensive entries in multValue
|
||||
* @property {Decimal} displayOverride If present, displays this string instead of multipliers. This
|
||||
* has higher priority than isBase
|
||||
* @property {Decimal|Number} fakeValue Value to be used as a stand-in for a total when this entry
|
||||
* is the parent resource of a list of other resources. Mostly used in entries that contribute to a whole differently
|
||||
* than how they're further broken down (eg. IP/EP contibuting as multipliers but consisting of currencies)
|
||||
* @property {Decimal|Number} multValue Value for multipliers given by this effect. Note that some
|
||||
* entries may have a pow10 applied to them in order to "undo" logarithmic scaling in the UI
|
||||
* @property {Number} powValue Numerical value for powers given by this effect
|
||||
* @property {Number} dilationEffect Exponent to use for dilation effect
|
||||
* @property {Boolean} isDilated Denotes if the multiplier is already dilated and needs an "anti-dilation"
|
||||
* calculation to be applied to make the numbers in the UI correct. Defaults to false
|
||||
* @property {Boolean} isActive Conditional determining if this component should be visible
|
||||
* @property {Array String} overlay String array to be used as HTML for an overlay on the tab; all
|
||||
* entries in the array are rendered on top of each other
|
||||
* @property {Object} icon An object containing text and color for the bar that this
|
||||
* entry has in the Vue component
|
||||
* }
|
||||
*/
|
||||
import "./general";
|
||||
import "./antimatter";
|
||||
import "./antimatter-dimensions";
|
||||
import "./infinity-dimensions";
|
||||
import "./time-dimensions";
|
||||
import "./infinity-points";
|
||||
import "./eternity-points";
|
||||
import "./tachyon-particles";
|
||||
import "./dilated-time";
|
||||
import "./tickspeed";
|
||||
import "./galaxies";
|
||||
import "./infinities";
|
||||
import "./eternities";
|
||||
import "./gamespeed";
|
||||
|
||||
// Some props in the tree are dynamically generated from value structure in the GameDB
|
||||
import "./tree";
|
54
javascripts/core/secret-formula/multiplier-tab/infinities.js
Normal file
54
javascripts/core/secret-formula/multiplier-tab/infinities.js
Normal file
@ -0,0 +1,54 @@
|
||||
import { DC } from "../../constants";
|
||||
import { GameDatabase } from "../game-database";
|
||||
|
||||
import { MultiplierTabIcons } from "./icons";
|
||||
|
||||
// See index.js for documentation
|
||||
GameDatabase.multiplierTabValues.infinities = {
|
||||
total: {
|
||||
name: "Infinities gained per Crunch",
|
||||
isBase: true,
|
||||
multValue: () => gainedInfinities(),
|
||||
isActive: () => Achievement(87).isUnlocked && !EternityChallenge(4).isRunning && !Pelle.isDoomed,
|
||||
overlay: ["∞", "<i class='fa-solid fa-arrows-rotate' />"],
|
||||
},
|
||||
achievement: {
|
||||
name: "Achievements",
|
||||
multValue: () => DC.D1.timesEffectsOf(
|
||||
Achievement(87),
|
||||
Achievement(164)
|
||||
),
|
||||
isActive: () => Achievement(87).isUnlocked,
|
||||
icon: MultiplierTabIcons.ACHIEVEMENT,
|
||||
},
|
||||
timeStudy: {
|
||||
name: "Time Study 32",
|
||||
multValue: () => TimeStudy(32).effectOrDefault(1),
|
||||
isActive: () => TimeStudy(32).isBought,
|
||||
icon: MultiplierTabIcons.TIME_STUDY,
|
||||
},
|
||||
realityUpgrades: {
|
||||
name: "Reality Upgrades",
|
||||
multValue: () => DC.D1.timesEffectsOf(RealityUpgrade(5), RealityUpgrade(7)),
|
||||
isActive: () => PlayerProgress.realityUnlocked(),
|
||||
icon: MultiplierTabIcons.UPGRADE("reality"),
|
||||
},
|
||||
glyph: {
|
||||
name: "Equipped Glyphs",
|
||||
multValue: () => getAdjustedGlyphEffect("infinityinfmult"),
|
||||
isActive: () => PlayerProgress.realityUnlocked(),
|
||||
icon: MultiplierTabIcons.GENERIC_GLYPH,
|
||||
},
|
||||
ra: {
|
||||
name: "Ra Boost from Time Theorems",
|
||||
multValue: () => Ra.unlocks.continuousTTBoost.effects.infinity.effectOrDefault(1),
|
||||
isActive: () => Ra.unlocks.continuousTTBoost.isUnlocked,
|
||||
icon: MultiplierTabIcons.GENERIC_RA,
|
||||
},
|
||||
singularity: {
|
||||
name: "Singularity Milestones",
|
||||
powValue: () => SingularityMilestone.infinitiedPow.effectOrDefault(1),
|
||||
isActive: () => SingularityMilestone.infinitiedPow.canBeApplied,
|
||||
icon: MultiplierTabIcons.SINGULARITY,
|
||||
},
|
||||
};
|
@ -0,0 +1,295 @@
|
||||
import { DC } from "../../constants";
|
||||
import { GameDatabase } from "../game-database";
|
||||
import { PlayerProgress } from "../../app/player-progress";
|
||||
|
||||
import { MultiplierTabHelper } from "./helper-functions";
|
||||
import { MultiplierTabIcons } from "./icons";
|
||||
|
||||
// See index.js for documentation
|
||||
GameDatabase.multiplierTabValues.ID = {
|
||||
total: {
|
||||
name: dim => {
|
||||
if (dim) return `ID ${dim} Multiplier`;
|
||||
if (EternityChallenge(7).isRunning) return "AD7 Production";
|
||||
return "Infinity Power Production";
|
||||
},
|
||||
displayOverride: dim => (dim
|
||||
? formatX(InfinityDimension(dim).multiplier, 2)
|
||||
: `${format(InfinityDimension(1).productionPerSecond, 2)}/sec`
|
||||
),
|
||||
multValue: dim => (dim
|
||||
? InfinityDimension(dim).multiplier
|
||||
: InfinityDimensions.all
|
||||
.filter(id => id.isProducing)
|
||||
.map(id => id.multiplier)
|
||||
.reduce((x, y) => x.times(y), DC.D1)),
|
||||
isActive: dim => (InfinityDimension(dim ?? 1).isProducing || EternityMilestone.autoUnlockID.isReached) &&
|
||||
!EternityChallenge(11).isRunning,
|
||||
dilationEffect: () => {
|
||||
const baseEff = player.dilation.active
|
||||
? 0.75 * Effects.product(DilationUpgrade.dilationPenalty)
|
||||
: 1;
|
||||
return baseEff * (Effarig.isRunning ? Effarig.multDilation : 1);
|
||||
},
|
||||
isDilated: true,
|
||||
overlay: ["∞", "<i class='fa-solid fa-cube' />"],
|
||||
icon: dim => MultiplierTabIcons.DIMENSION("ID", dim),
|
||||
},
|
||||
purchase: {
|
||||
name: dim => (dim ? `Purchased ID ${dim}` : "Purchases"),
|
||||
multValue: dim => {
|
||||
const getMult = id => Decimal.pow(InfinityDimension(id).powerMultiplier,
|
||||
Math.floor(InfinityDimension(id).baseAmount / 10));
|
||||
if (dim) return getMult(dim);
|
||||
return InfinityDimensions.all
|
||||
.filter(id => id.isProducing)
|
||||
.map(id => getMult(id.tier))
|
||||
.reduce((x, y) => x.times(y), DC.D1);
|
||||
},
|
||||
isActive: () => !EternityChallenge(2).isRunning && !EternityChallenge(10).isRunning,
|
||||
icon: dim => MultiplierTabIcons.PURCHASE("ID", dim),
|
||||
},
|
||||
highestDim: {
|
||||
name: () => `Amount of highest Dimension`,
|
||||
displayOverride: () => {
|
||||
const dim = MultiplierTabHelper.activeDimCount("ID");
|
||||
return `ID ${dim}, ${format(InfinityDimension(dim).amount, 2)}`;
|
||||
},
|
||||
multValue: () => InfinityDimension(MultiplierTabHelper.activeDimCount("ID")).amount,
|
||||
isActive: () => InfinityDimension(1).isProducing,
|
||||
icon: MultiplierTabIcons.DIMENSION("ID"),
|
||||
},
|
||||
|
||||
basePurchase: {
|
||||
name: "Base purchases",
|
||||
multValue: dim => {
|
||||
const getMult = id => {
|
||||
const purchases = id === 8
|
||||
? Math.floor(InfinityDimension(id).baseAmount / 10)
|
||||
: Math.min(InfinityDimensions.HARDCAP_PURCHASES, Math.floor(InfinityDimension(id).baseAmount / 10));
|
||||
const baseMult = InfinityDimension(id)._powerMultiplier;
|
||||
return Decimal.pow(baseMult, purchases);
|
||||
};
|
||||
if (dim) return getMult(dim);
|
||||
return InfinityDimensions.all
|
||||
.filter(id => id.isProducing)
|
||||
.map(id => getMult(id.tier))
|
||||
.reduce((x, y) => x.times(y), DC.D1);
|
||||
},
|
||||
isActive: dim => ImaginaryUpgrade(14).canBeApplied ||
|
||||
(dim === 8 ? GlyphSacrifice.infinity.effectValue > 1 : Tesseracts.bought > 0),
|
||||
icon: MultiplierTabIcons.PURCHASE("baseID"),
|
||||
},
|
||||
tesseractPurchase: {
|
||||
name: "Tesseracts",
|
||||
multValue: dim => {
|
||||
const getMult = id => {
|
||||
if (id === 8) return DC.D1;
|
||||
const purchases = Math.floor(InfinityDimension(id).baseAmount / 10);
|
||||
return Decimal.pow(InfinityDimension(id).powerMultiplier,
|
||||
Math.clampMin(purchases - InfinityDimensions.HARDCAP_PURCHASES, 0));
|
||||
};
|
||||
if (dim) return getMult(dim);
|
||||
return InfinityDimensions.all
|
||||
.filter(id => id.isProducing)
|
||||
.map(id => getMult(id.tier))
|
||||
.reduce((x, y) => x.times(y), DC.D1);
|
||||
},
|
||||
isActive: () => Tesseracts.bought > 0,
|
||||
icon: MultiplierTabIcons.PURCHASE("tesseractID"),
|
||||
},
|
||||
infinityGlyphSacrifice: {
|
||||
name: "Infinity Glyph sacrifice",
|
||||
multValue: () => (InfinityDimension(8).isProducing
|
||||
? Decimal.pow(GlyphSacrifice.infinity.effectValue, Math.floor(InfinityDimension(8).baseAmount / 10))
|
||||
: DC.D1),
|
||||
isActive: () => GlyphSacrifice.infinity.effectValue > 1,
|
||||
icon: MultiplierTabIcons.SACRIFICE("infinity"),
|
||||
},
|
||||
powPurchase: {
|
||||
name: "Reflection of Intrusion",
|
||||
powValue: () => ImaginaryUpgrade(14).effectOrDefault(1),
|
||||
isActive: () => ImaginaryUpgrade(14).canBeApplied,
|
||||
icon: MultiplierTabIcons.UPGRADE("imaginary"),
|
||||
},
|
||||
|
||||
replicanti: {
|
||||
name: "Replicanti Multiplier",
|
||||
multValue: dim => Decimal.pow(replicantiMult(), dim ? 1 : MultiplierTabHelper.activeDimCount("ID")),
|
||||
isActive: () => Replicanti.areUnlocked,
|
||||
icon: MultiplierTabIcons.SPECIFIC_GLYPH("replication"),
|
||||
},
|
||||
achievementMult: {
|
||||
name: "Achievement Multiplier",
|
||||
multValue: dim => Decimal.pow(Achievements.power, dim ? 1 : MultiplierTabHelper.activeDimCount("ID")),
|
||||
isActive: () => Achievement(75).canBeApplied && !Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.ACHIEVEMENT,
|
||||
},
|
||||
achievement: {
|
||||
// Note: This only applies to ID1
|
||||
name: () => "Achievement 94",
|
||||
multValue: dim => ((dim ?? 1) === 1 ? Achievement(94).effectOrDefault(1) : 1),
|
||||
isActive: () => Achievement(94).canBeApplied,
|
||||
icon: MultiplierTabIcons.ACHIEVEMENT,
|
||||
},
|
||||
timeStudy: {
|
||||
name: dim => (dim ? `Time Studies (ID ${dim})` : "Time Studies"),
|
||||
multValue: dim => {
|
||||
const allMult = DC.D1.timesEffectsOf(
|
||||
TimeStudy(82),
|
||||
TimeStudy(92),
|
||||
TimeStudy(162)
|
||||
);
|
||||
if (dim) return dim === 4 ? allMult.times(TimeStudy(72).effectOrDefault(1)) : allMult;
|
||||
const maxActiveDim = MultiplierTabHelper.activeDimCount("ID");
|
||||
return Decimal.pow(allMult, maxActiveDim).times(maxActiveDim >= 4 ? TimeStudy(72).effectOrDefault(1) : DC.D1);
|
||||
},
|
||||
isActive: () => PlayerProgress.eternityUnlocked(),
|
||||
icon: MultiplierTabIcons.TIME_STUDY,
|
||||
},
|
||||
eternityUpgrade: {
|
||||
name: "Eternity Upgrades",
|
||||
multValue: dim => {
|
||||
const allMult = DC.D1.timesEffectsOf(
|
||||
EternityUpgrade.idMultEP,
|
||||
EternityUpgrade.idMultEternities,
|
||||
EternityUpgrade.idMultICRecords,
|
||||
);
|
||||
return Decimal.pow(allMult, dim ? 1 : MultiplierTabHelper.activeDimCount("ID"));
|
||||
},
|
||||
isActive: () => PlayerProgress.eternityUnlocked(),
|
||||
icon: MultiplierTabIcons.UPGRADE("eternity"),
|
||||
},
|
||||
|
||||
eu1: {
|
||||
name: () => "Unspent Eternity Points",
|
||||
multValue: dim => Decimal.pow(EternityUpgrade.idMultEP.effectOrDefault(1),
|
||||
dim ? 1 : MultiplierTabHelper.activeDimCount("ID")),
|
||||
isActive: () => EternityUpgrade.idMultEP.canBeApplied,
|
||||
icon: MultiplierTabIcons.UPGRADE("eternity"),
|
||||
},
|
||||
eu2: {
|
||||
name: () => "Eternity Count",
|
||||
multValue: dim => Decimal.pow(EternityUpgrade.idMultEternities.effectOrDefault(1),
|
||||
dim ? 1 : MultiplierTabHelper.activeDimCount("ID")),
|
||||
isActive: () => EternityUpgrade.idMultEternities.canBeApplied,
|
||||
icon: MultiplierTabIcons.UPGRADE("eternity"),
|
||||
},
|
||||
eu3: {
|
||||
name: () => "Infinity Challenge Records",
|
||||
multValue: dim => Decimal.pow(EternityUpgrade.idMultICRecords.effectOrDefault(1),
|
||||
dim ? 1 : MultiplierTabHelper.activeDimCount("ID")),
|
||||
isActive: () => EternityUpgrade.idMultICRecords.canBeApplied,
|
||||
icon: MultiplierTabIcons.UPGRADE("eternity"),
|
||||
},
|
||||
|
||||
infinityChallenge: {
|
||||
name: "Infinity Challenges",
|
||||
multValue: dim => {
|
||||
const allMult = DC.D1.timesEffectsOf(
|
||||
InfinityChallenge(1).reward,
|
||||
InfinityChallenge(6).reward,
|
||||
);
|
||||
return Decimal.pow(allMult, dim ? 1 : MultiplierTabHelper.activeDimCount("ID"));
|
||||
},
|
||||
isActive: () => InfinityChallenge(1).isCompleted,
|
||||
icon: MultiplierTabIcons.CHALLENGE("infinity"),
|
||||
},
|
||||
eternityChallenge: {
|
||||
name: dim => (dim ? `Eternity Challenges (ID ${dim})` : " Eternity Challenges"),
|
||||
multValue: dim => {
|
||||
const allMult = DC.D1.timesEffectsOf(
|
||||
EternityChallenge(4).reward,
|
||||
EternityChallenge(9).reward,
|
||||
).times(EternityChallenge(7).isRunning ? Tickspeed.perSecond : DC.D1);
|
||||
if (dim) {
|
||||
if (dim === 1) return allMult.times(EternityChallenge(2).reward.effectOrDefault(1));
|
||||
return allMult;
|
||||
}
|
||||
const maxActiveDim = MultiplierTabHelper.activeDimCount("ID");
|
||||
return Decimal.pow(allMult, maxActiveDim)
|
||||
.times(maxActiveDim >= 1 ? EternityChallenge(2).reward.effectOrDefault(1) : DC.D1);
|
||||
},
|
||||
isActive: () => EternityChallenge(2).completions > 0,
|
||||
icon: MultiplierTabIcons.CHALLENGE("eternity"),
|
||||
},
|
||||
tickspeed: {
|
||||
name: () => "Tickspeed (EC7)",
|
||||
displayOverride: () => {
|
||||
const tickRate = Tickspeed.perSecond;
|
||||
const activeDims = MultiplierTabHelper.activeDimCount("ID");
|
||||
return `${format(tickRate, 2, 2)}/sec on ${formatInt(activeDims)} ${pluralize("Dimension", activeDims)}
|
||||
➜ ${formatX(tickRate.pow(activeDims), 2, 2)}`;
|
||||
},
|
||||
multValue: () => Tickspeed.perSecond.pow(8),
|
||||
isActive: () => EternityChallenge(7).isRunning,
|
||||
icon: MultiplierTabIcons.TICKSPEED,
|
||||
},
|
||||
glyph: {
|
||||
name: "Glyph Effects",
|
||||
multValue: () => 1,
|
||||
powValue: () => getAdjustedGlyphEffect("infinitypow") * getAdjustedGlyphEffect("effarigdimensions"),
|
||||
isActive: () => PlayerProgress.realityUnlocked(),
|
||||
icon: MultiplierTabIcons.GENERIC_GLYPH,
|
||||
},
|
||||
alchemy: {
|
||||
name: "Glyph Alchemy",
|
||||
multValue: dim => Decimal.pow(AlchemyResource.dimensionality.effectOrDefault(1),
|
||||
dim ? 1 : MultiplierTabHelper.activeDimCount("ID")),
|
||||
powValue: () => AlchemyResource.infinity.effectOrDefault(1) * Ra.momentumValue,
|
||||
isActive: () => Ra.unlocks.unlockGlyphAlchemy.canBeApplied,
|
||||
icon: MultiplierTabIcons.ALCHEMY,
|
||||
},
|
||||
imaginaryUpgrade: {
|
||||
name: "Hyperbolic Apeirogon",
|
||||
multValue: dim => Decimal.pow(ImaginaryUpgrade(8).effectOrDefault(1),
|
||||
dim ? 1 : MultiplierTabHelper.activeDimCount("ID")),
|
||||
isActive: () => ImaginaryUpgrade(8).canBeApplied,
|
||||
icon: MultiplierTabIcons.UPGRADE("imaginary"),
|
||||
},
|
||||
pelle: {
|
||||
name: "Pelle Rift Effects",
|
||||
multValue: dim => {
|
||||
const mult = DC.D1.timesEffectsOf(PelleRifts.recursion.milestones[1]);
|
||||
const maxActiveDim = MultiplierTabHelper.activeDimCount("ID");
|
||||
return Decimal.pow(mult, dim ? 1 : maxActiveDim)
|
||||
.times(maxActiveDim >= 1 ? PelleRifts.decay.milestones[0].effectOrDefault(1) : DC.D1);
|
||||
},
|
||||
powValue: () => PelleRifts.paradox.effectOrDefault(DC.D1).toNumber(),
|
||||
isActive: () => Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.PELLE,
|
||||
},
|
||||
iap: {
|
||||
name: "Shop Tab Purchases",
|
||||
multValue: dim => Decimal.pow(ShopPurchase.allDimPurchases.currentMult,
|
||||
dim ? 1 : MultiplierTabHelper.activeDimCount("ID")),
|
||||
isActive: () => ShopPurchaseData.totalSTD > 0,
|
||||
icon: MultiplierTabIcons.IAP,
|
||||
},
|
||||
|
||||
powerConversion: {
|
||||
name: "Infinity Power Conversion",
|
||||
powValue: () => InfinityDimensions.powerConversionRate,
|
||||
isActive: () => Currency.infinityPower.value.gt(1) && !EternityChallenge(9).isRunning,
|
||||
icon: MultiplierTabIcons.IPOW_CONVERSION,
|
||||
},
|
||||
|
||||
nerfV: {
|
||||
name: "V's Reality",
|
||||
powValue: () => 0.5,
|
||||
isActive: () => V.isRunning,
|
||||
icon: MultiplierTabIcons.GENERIC_V,
|
||||
},
|
||||
nerfCursed: {
|
||||
name: "Cursed Glyphs",
|
||||
powValue: () => getAdjustedGlyphEffect("curseddimensions"),
|
||||
isActive: () => getAdjustedGlyphEffect("curseddimensions") !== 1,
|
||||
icon: MultiplierTabIcons.SPECIFIC_GLYPH("cursed"),
|
||||
},
|
||||
nerfPelle: {
|
||||
name: "Doomed Nerfs",
|
||||
powValue: 0.5,
|
||||
isActive: () => PelleStrikes.powerGalaxies.hasStrike,
|
||||
icon: MultiplierTabIcons.PELLE,
|
||||
}
|
||||
};
|
@ -0,0 +1,121 @@
|
||||
import { DC } from "../../constants";
|
||||
import { GameDatabase } from "../game-database";
|
||||
import { PlayerProgress } from "../../app/player-progress";
|
||||
|
||||
import { MultiplierTabIcons } from "./icons";
|
||||
|
||||
// See index.js for documentation
|
||||
GameDatabase.multiplierTabValues.IP = {
|
||||
total: {
|
||||
name: "Total IP Gained on Infinity",
|
||||
isBase: true,
|
||||
multValue: () => gainedInfinityPoints(),
|
||||
isActive: () => new Decimal(Currency.infinities.value).gt(0) || gainedInfinityPoints().gt(0),
|
||||
dilationEffect: () => (Laitela.isRunning ? 0.75 * Effects.product(DilationUpgrade.dilationPenalty) : 1),
|
||||
isDilated: true,
|
||||
overlay: ["∞", "<i class='fa-solid fa-layer-group' />"],
|
||||
},
|
||||
base: {
|
||||
name: "Base Infinity Points",
|
||||
isBase: true,
|
||||
fakeValue: DC.D5,
|
||||
multValue: () => {
|
||||
const div = Effects.min(308, Achievement(103), TimeStudy(111));
|
||||
return Decimal.pow10(player.records.thisInfinity.maxAM.log10() / div - 0.75);
|
||||
},
|
||||
isActive: () => player.break,
|
||||
icon: MultiplierTabIcons.CONVERT_FROM("AM"),
|
||||
},
|
||||
antimatter: {
|
||||
name: "Infinity Points from Antimatter",
|
||||
displayOverride: () => `${format(player.records.thisInfinity.maxAM, 2, 2)} AM`,
|
||||
// Just needs to match the value in base and be larger than 1
|
||||
multValue: DC.D5,
|
||||
isActive: () => player.break,
|
||||
icon: MultiplierTabIcons.ANTIMATTER,
|
||||
},
|
||||
divisor: {
|
||||
name: "Formula Improvement",
|
||||
displayOverride: () => {
|
||||
const div = Effects.min(308, Achievement(103), TimeStudy(111));
|
||||
return `log(AM)/${formatInt(308)} ➜ log(AM)/${format(div, 2, 1)}`;
|
||||
},
|
||||
powValue: () => 308 / Effects.min(308, Achievement(103), TimeStudy(111)),
|
||||
isActive: () => Achievement(103).canBeApplied || TimeStudy(111).isBought,
|
||||
icon: MultiplierTabIcons.DIVISOR("IP"),
|
||||
},
|
||||
infinityUpgrade: {
|
||||
name: () => `Repeatable ${formatX(2)} Infinity Upgrade`,
|
||||
multValue: () => InfinityUpgrade.ipMult.effectOrDefault(1),
|
||||
isActive: () => player.break && !Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.UPGRADE("infinity"),
|
||||
},
|
||||
achievement: {
|
||||
name: "Achievements",
|
||||
multValue: () => DC.D1.timesEffectsOf(
|
||||
Achievement(85),
|
||||
Achievement(93),
|
||||
Achievement(116),
|
||||
Achievement(125),
|
||||
Achievement(141).effects.ipGain,
|
||||
),
|
||||
isActive: () => player.break && !Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.ACHIEVEMENT,
|
||||
},
|
||||
timeStudy: {
|
||||
name: "Time Studies",
|
||||
multValue: () => DC.D1.timesEffectsOf(
|
||||
TimeStudy(41),
|
||||
TimeStudy(51),
|
||||
TimeStudy(141),
|
||||
TimeStudy(142),
|
||||
TimeStudy(143),
|
||||
),
|
||||
isActive: () => player.break && !Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.TIME_STUDY,
|
||||
},
|
||||
dilationUpgrade: {
|
||||
name: "Dilation Upgrade (Based on DT)",
|
||||
multValue: () => DilationUpgrade.ipMultDT.effectOrDefault(1),
|
||||
isActive: () => DilationUpgrade.ipMultDT.canBeApplied,
|
||||
icon: MultiplierTabIcons.UPGRADE("dilation"),
|
||||
},
|
||||
glyph: {
|
||||
name: "Equipped Glyphs",
|
||||
multValue: () => Pelle.specialGlyphEffect.infinity.times(getAdjustedGlyphEffect("infinityIP")),
|
||||
powValue: () => (GlyphAlteration.isAdded("infinity") ? getSecondaryGlyphEffect("infinityIP") : 1),
|
||||
isActive: () => PlayerProgress.realityUnlocked() && !Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.GENERIC_GLYPH,
|
||||
},
|
||||
alchemy: {
|
||||
name: "Glyph Alchemy",
|
||||
multValue: () => Replicanti.amount.powEffectOf(AlchemyResource.exponential),
|
||||
isActive: () => Ra.unlocks.unlockGlyphAlchemy.canBeApplied,
|
||||
icon: MultiplierTabIcons.ALCHEMY,
|
||||
},
|
||||
pelle: {
|
||||
name: "Pelle Rift Effects",
|
||||
multValue: () => DC.D1.timesEffectsOf(PelleRifts.vacuum).times(Pelle.specialGlyphEffect.infinity),
|
||||
isActive: () => Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.PELLE,
|
||||
},
|
||||
iap: {
|
||||
name: "Shop Tab Purchases",
|
||||
multValue: () => ShopPurchase.IPPurchases.currentMult,
|
||||
isActive: () => ShopPurchaseData.totalSTD > 0,
|
||||
icon: MultiplierTabIcons.IAP,
|
||||
},
|
||||
|
||||
nerfTeresa: {
|
||||
name: "Teresa's Reality",
|
||||
powValue: () => 0.55,
|
||||
isActive: () => Teresa.isRunning,
|
||||
icon: MultiplierTabIcons.GENERIC_TERESA,
|
||||
},
|
||||
nerfV: {
|
||||
name: "V's Reality",
|
||||
powValue: () => 0.5,
|
||||
isActive: () => V.isRunning,
|
||||
icon: MultiplierTabIcons.GENERIC_V,
|
||||
},
|
||||
};
|
@ -0,0 +1,66 @@
|
||||
import { DC } from "../../constants";
|
||||
import { GameDatabase } from "../game-database";
|
||||
import { PlayerProgress } from "../../app/player-progress";
|
||||
|
||||
import { MultiplierTabIcons } from "./icons";
|
||||
|
||||
// See index.js for documentation
|
||||
GameDatabase.multiplierTabValues.TP = {
|
||||
total: {
|
||||
name: "Total Tachyon Particles",
|
||||
displayOverride: () => {
|
||||
const baseTPStr = format(new Decimal(Currency.tachyonParticles.value), 2, 2);
|
||||
return PelleRifts.paradox.milestones[1].canBeApplied
|
||||
? `${baseTPStr}${formatPow(PelleRifts.paradox.milestones[1].effectValue, 1, 1)}`
|
||||
: baseTPStr;
|
||||
},
|
||||
multValue: () => new Decimal(Currency.tachyonParticles.value)
|
||||
.pow(PelleRifts.paradox.milestones[1].effectOrDefault(1)),
|
||||
isActive: () => new Decimal(Currency.tachyonParticles.value).gt(0),
|
||||
icon: MultiplierTabIcons.TACHYON_PARTICLES,
|
||||
},
|
||||
base: {
|
||||
name: "Base Tachyon Particle Count",
|
||||
isBase: true,
|
||||
multValue: () => new Decimal(Currency.tachyonParticles.value).div(tachyonGainMultiplier()),
|
||||
isActive: () => new Decimal(Currency.tachyonParticles.value).gt(0),
|
||||
icon: MultiplierTabIcons.TACHYON_PARTICLES,
|
||||
},
|
||||
achievementMult: {
|
||||
name: "Achievement Multiplier",
|
||||
multValue: () => RealityUpgrade(8).effectOrDefault(1),
|
||||
isActive: () => RealityUpgrade(8).canBeApplied && !Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.ACHIEVEMENT,
|
||||
},
|
||||
achievement: {
|
||||
name: "Achievement 132 Reward",
|
||||
multValue: () => Achievement(132).effectOrDefault(1),
|
||||
isActive: () => Achievement(132).canBeApplied,
|
||||
icon: MultiplierTabIcons.ACHIEVEMENT,
|
||||
},
|
||||
dilation: {
|
||||
name: `Dilation Upgrade (Repeatable TP multiplier)`,
|
||||
multValue: () => DilationUpgrade.tachyonGain.effectOrDefault(1),
|
||||
isActive: () => DilationUpgrade.tachyonGain.canBeApplied,
|
||||
icon: MultiplierTabIcons.UPGRADE("dilation"),
|
||||
},
|
||||
realityUpgrade: {
|
||||
name: "Reality Upgrades",
|
||||
multValue: () => DC.D1.timesEffectsOf(RealityUpgrade(4), RealityUpgrade(15)),
|
||||
isActive: () => PlayerProgress.realityUnlocked() && !Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.UPGRADE("reality"),
|
||||
},
|
||||
dilationGlyphSacrifice: {
|
||||
name: "Dilation Glyph Sacrifice",
|
||||
multValue: () => GlyphSacrifice.dilation.effectValue,
|
||||
isActive: () => GlyphSacrifice.dilation.effectValue > 1,
|
||||
icon: MultiplierTabIcons.SACRIFICE("dilation"),
|
||||
},
|
||||
|
||||
nerfEnslaved: {
|
||||
name: "The Nameless Ones' Reality",
|
||||
powValue: () => Enslaved.tachyonNerf,
|
||||
isActive: () => Enslaved.isRunning,
|
||||
icon: MultiplierTabIcons.GENERIC_ENSLAVED,
|
||||
}
|
||||
};
|
81
javascripts/core/secret-formula/multiplier-tab/tickspeed.js
Normal file
81
javascripts/core/secret-formula/multiplier-tab/tickspeed.js
Normal file
@ -0,0 +1,81 @@
|
||||
import { DC } from "../../constants";
|
||||
import { GameDatabase } from "../game-database";
|
||||
|
||||
import { MultiplierTabHelper } from "./helper-functions";
|
||||
import { MultiplierTabIcons } from "./icons";
|
||||
|
||||
// See index.js for documentation
|
||||
GameDatabase.multiplierTabValues.tickspeed = {
|
||||
total: {
|
||||
name: "Total Tickspeed",
|
||||
displayOverride: () => {
|
||||
const tickRate = Tickspeed.perSecond;
|
||||
const activeDims = MultiplierTabHelper.activeDimCount("AD");
|
||||
return `${format(tickRate, 2, 2)}/sec on ${formatInt(activeDims)} ${pluralize("Dimension", activeDims)}
|
||||
➜ ${formatX(tickRate.pow(activeDims), 2, 2)}`;
|
||||
},
|
||||
// This is necessary to make multValue entries from the other props scale properly, which are also all pow10
|
||||
// due to the multiplier tab splitting up entries logarithmically
|
||||
fakeValue: DC.E100,
|
||||
multValue: () => Tickspeed.perSecond.pow(MultiplierTabHelper.activeDimCount("AD")),
|
||||
// No point in showing this breakdown at all unless both components are nonzero; however they will always be nonzero
|
||||
// due to the way the calculation works, so we have to manually hide it here
|
||||
isActive: () => Tickspeed.perSecond.gt(1) && effectiveBaseGalaxies() > 0,
|
||||
dilationEffect: () => (Effarig.isRunning ? Effarig.tickDilation : 1),
|
||||
overlay: ["<i class='fa-solid fa-clock' />"],
|
||||
icon: MultiplierTabIcons.TICKSPEED,
|
||||
},
|
||||
base: {
|
||||
name: "Base Tickspeed from Achievements",
|
||||
displayOverride: () => {
|
||||
const val = DC.D1.dividedByEffectsOf(
|
||||
Achievement(36),
|
||||
Achievement(45),
|
||||
Achievement(66),
|
||||
Achievement(83)
|
||||
);
|
||||
return `${format(val, 2, 2)}/sec`;
|
||||
},
|
||||
multValue: () => new Decimal.pow10(100 * MultiplierTabHelper.decomposeTickspeed().base),
|
||||
isActive: () => [36, 45, 66, 83].some(a => Achievement(a).canBeApplied),
|
||||
icon: MultiplierTabIcons.ACHIEVEMENT,
|
||||
},
|
||||
upgrades: {
|
||||
name: "Tickspeed Upgrades",
|
||||
displayOverride: () => `${formatInt(Tickspeed.totalUpgrades)} Total`,
|
||||
multValue: () => new Decimal.pow10(100 * MultiplierTabHelper.decomposeTickspeed().tickspeed),
|
||||
isActive: true,
|
||||
icon: MultiplierTabIcons.PURCHASE("AD"),
|
||||
},
|
||||
galaxies: {
|
||||
name: "Galaxies",
|
||||
displayOverride: () => {
|
||||
const ag = player.galaxies + GalaxyGenerator.galaxies;
|
||||
const rg = Replicanti.galaxies.total;
|
||||
const tg = player.dilation.totalTachyonGalaxies;
|
||||
return `${formatInt(ag + rg + tg)} Total`;
|
||||
},
|
||||
multValue: () => new Decimal.pow10(100 * MultiplierTabHelper.decomposeTickspeed().galaxies),
|
||||
isActive: true,
|
||||
icon: MultiplierTabIcons.GALAXY,
|
||||
},
|
||||
};
|
||||
|
||||
GameDatabase.multiplierTabValues.tickspeedUpgrades = {
|
||||
purchased: {
|
||||
name: "Purchased Tickspeed Upgrades",
|
||||
displayOverride: () => (Laitela.continuumActive
|
||||
? formatFloat(Tickspeed.continuumValue, 2, 2)
|
||||
: formatInt(player.totalTickBought)),
|
||||
multValue: () => Decimal.pow10(Laitela.continuumActive ? Tickspeed.continuumValue : player.totalTickBought),
|
||||
isActive: () => true,
|
||||
icon: MultiplierTabIcons.PURCHASE("AD"),
|
||||
},
|
||||
free: {
|
||||
name: "Tickspeed Upgrades from TD",
|
||||
displayOverride: () => formatInt(player.totalTickGained),
|
||||
multValue: () => Decimal.pow10(player.totalTickGained),
|
||||
isActive: () => Currency.timeShards.gt(0),
|
||||
icon: MultiplierTabIcons.SPECIFIC_GLYPH("time"),
|
||||
}
|
||||
};
|
@ -0,0 +1,265 @@
|
||||
import { DC } from "../../constants";
|
||||
import { GameDatabase } from "../game-database";
|
||||
import { PlayerProgress } from "../../app/player-progress";
|
||||
|
||||
import { MultiplierTabHelper } from "./helper-functions";
|
||||
import { MultiplierTabIcons } from "./icons";
|
||||
|
||||
// See index.js for documentation
|
||||
GameDatabase.multiplierTabValues.TD = {
|
||||
total: {
|
||||
name: dim => {
|
||||
if (dim) return `TD ${dim} Multiplier`;
|
||||
if (EternityChallenge(7).isRunning) return "ID8 Production";
|
||||
return "Time Shard Production";
|
||||
},
|
||||
displayOverride: dim => (dim
|
||||
? formatX(TimeDimension(dim).multiplier, 2)
|
||||
: `${format(TimeDimension(1).productionPerSecond, 2)}/sec`
|
||||
),
|
||||
multValue: dim => (dim
|
||||
? TimeDimension(dim).multiplier
|
||||
: TimeDimensions.all
|
||||
.filter(td => td.isProducing)
|
||||
.map(td => td.multiplier)
|
||||
.reduce((x, y) => x.times(y), DC.D1)),
|
||||
isActive: dim => TimeDimension(dim ?? 1).isProducing && !EternityChallenge(11).isRunning,
|
||||
dilationEffect: () => {
|
||||
const baseEff = player.dilation.active
|
||||
? 0.75 * Effects.product(DilationUpgrade.dilationPenalty)
|
||||
: 1;
|
||||
return baseEff * (Effarig.isRunning ? Effarig.multDilation : 1);
|
||||
},
|
||||
isDilated: true,
|
||||
overlay: ["Δ", "<i class='fa-solid fa-cube' />"],
|
||||
icon: dim => MultiplierTabIcons.DIMENSION("TD", dim),
|
||||
},
|
||||
purchase: {
|
||||
name: dim => (dim ? `Purchased TD ${dim}` : "Purchases"),
|
||||
multValue: dim => {
|
||||
const getMult = td => {
|
||||
const d = TimeDimension(td);
|
||||
const bought = td === 8 ? Math.clampMax(d.bought, 1e8) : d.bought;
|
||||
return Decimal.pow(d.powerMultiplier, bought);
|
||||
};
|
||||
if (dim) return getMult(dim);
|
||||
return TimeDimensions.all
|
||||
.filter(td => td.isProducing)
|
||||
.map(td => getMult(td.tier))
|
||||
.reduce((x, y) => x.times(y), DC.D1);
|
||||
},
|
||||
isActive: () => !EternityChallenge(2).isRunning && !EternityChallenge(10).isRunning,
|
||||
icon: dim => MultiplierTabIcons.PURCHASE("TD", dim),
|
||||
},
|
||||
highestDim: {
|
||||
name: () => `Amount of highest Dimension`,
|
||||
displayOverride: () => {
|
||||
const dim = MultiplierTabHelper.activeDimCount("TD");
|
||||
return `TD ${dim}, ${formatInt(TimeDimension(dim).amount)}`;
|
||||
},
|
||||
multValue: () => TimeDimension(MultiplierTabHelper.activeDimCount("TD")).amount,
|
||||
isActive: () => TimeDimension(1).isProducing,
|
||||
icon: MultiplierTabIcons.DIMENSION("TD"),
|
||||
},
|
||||
|
||||
basePurchase: {
|
||||
name: "Base purchases",
|
||||
multValue: dim => {
|
||||
const getMult = td => Decimal.pow(4,
|
||||
td === 8 ? Math.clampMax(TimeDimension(td).bought, 1e8) : TimeDimension(td).bought);
|
||||
if (dim) return getMult(dim);
|
||||
return TimeDimensions.all
|
||||
.filter(td => td.isProducing)
|
||||
.map(td => getMult(td.tier))
|
||||
.reduce((x, y) => x.times(y), DC.D1);
|
||||
},
|
||||
isActive: dim => (dim
|
||||
? ImaginaryUpgrade(14).canBeApplied || (dim === 8 && GlyphSacrifice.time.effectValue > 1)
|
||||
: TimeDimension(1).isProducing),
|
||||
icon: dim => MultiplierTabIcons.PURCHASE("TD", dim),
|
||||
},
|
||||
timeGlyphSacrifice: {
|
||||
name: "Time Glyph Sacrifice",
|
||||
multValue: () => (TimeDimension(8).isProducing
|
||||
? Decimal.pow(GlyphSacrifice.time.effectValue, Math.clampMax(TimeDimension(8).bought, 1e8))
|
||||
: DC.D1),
|
||||
isActive: () => GlyphSacrifice.time.effectValue > 1,
|
||||
icon: MultiplierTabIcons.SACRIFICE("time"),
|
||||
},
|
||||
powPurchase: {
|
||||
name: "Reflection of Intrusion",
|
||||
powValue: () => ImaginaryUpgrade(14).effectOrDefault(1),
|
||||
isActive: () => ImaginaryUpgrade(14).canBeApplied,
|
||||
icon: MultiplierTabIcons.UPGRADE("imaginary"),
|
||||
},
|
||||
|
||||
achievementMult: {
|
||||
name: "Achievement Multiplier",
|
||||
multValue: dim => Decimal.pow(EternityUpgrade.tdMultAchs.effectOrDefault(1),
|
||||
dim ? 1 : MultiplierTabHelper.activeDimCount("TD")),
|
||||
isActive: () => EternityUpgrade.tdMultAchs.canBeApplied && !Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.ACHIEVEMENT,
|
||||
},
|
||||
achievement: {
|
||||
name: "Achievement Rewards",
|
||||
multValue: dim => {
|
||||
const baseMult = DC.D1.timesEffectsOf(Achievement(105), Achievement(128));
|
||||
return Decimal.pow(baseMult, dim ? 1 : MultiplierTabHelper.activeDimCount("TD"));
|
||||
},
|
||||
isActive: () => Achievement(105).canBeApplied || Achievement(128).canBeApplied,
|
||||
icon: MultiplierTabIcons.ACHIEVEMENT,
|
||||
},
|
||||
timeStudy: {
|
||||
name: dim => (dim ? `Time Studies (TD ${dim})` : "Time Studies"),
|
||||
multValue: dim => {
|
||||
const allMult = DC.D1.timesEffectsOf(
|
||||
TimeStudy(93),
|
||||
TimeStudy(103),
|
||||
TimeStudy(151),
|
||||
TimeStudy(221),
|
||||
TimeStudy(301),
|
||||
);
|
||||
|
||||
const dimMults = Array.repeat(DC.D1, 9);
|
||||
for (let tier = 1; tier <= 8; tier++) {
|
||||
dimMults[tier] = dimMults[tier].timesEffectsOf(
|
||||
tier === 1 ? TimeStudy(11) : null,
|
||||
tier === 3 ? TimeStudy(73) : null,
|
||||
tier === 4 ? TimeStudy(227) : null
|
||||
);
|
||||
}
|
||||
|
||||
if (dim) return allMult.times(dimMults[dim]);
|
||||
let totalMult = DC.D1;
|
||||
for (let tier = 1; tier <= MultiplierTabHelper.activeDimCount("TD"); tier++) {
|
||||
totalMult = totalMult.times(dimMults[tier]).times(allMult);
|
||||
}
|
||||
return totalMult;
|
||||
},
|
||||
isActive: () => TimeDimension(1).isProducing,
|
||||
icon: MultiplierTabIcons.TIME_STUDY
|
||||
},
|
||||
eternityUpgrade: {
|
||||
name: dim => (dim ? `Eternity Upgrades (TD ${dim})` : "Eternity Upgrades"),
|
||||
multValue: dim => {
|
||||
const allMult = DC.D1.timesEffectsOf(
|
||||
EternityUpgrade.tdMultTheorems,
|
||||
EternityUpgrade.tdMultRealTime,
|
||||
);
|
||||
return Decimal.pow(allMult, dim ? 1 : MultiplierTabHelper.activeDimCount("TD"));
|
||||
},
|
||||
isActive: () => TimeDimension(1).isProducing,
|
||||
icon: MultiplierTabIcons.UPGRADE("eternity"),
|
||||
},
|
||||
|
||||
eu1: {
|
||||
name: () => "Unspent Time Theorems",
|
||||
multValue: dim => Decimal.pow(EternityUpgrade.tdMultTheorems.effectOrDefault(1),
|
||||
dim ? 1 : MultiplierTabHelper.activeDimCount("TD")),
|
||||
isActive: () => EternityUpgrade.tdMultTheorems.canBeApplied,
|
||||
icon: MultiplierTabIcons.UPGRADE("eternity"),
|
||||
},
|
||||
eu2: {
|
||||
name: () => "Days played",
|
||||
multValue: dim => Decimal.pow(EternityUpgrade.tdMultRealTime.effectOrDefault(1),
|
||||
dim ? 1 : MultiplierTabHelper.activeDimCount("TD")),
|
||||
isActive: () => EternityUpgrade.tdMultRealTime.canBeApplied,
|
||||
icon: MultiplierTabIcons.UPGRADE("eternity"),
|
||||
},
|
||||
|
||||
eternityChallenge: {
|
||||
name: dim => (dim ? `Eternity Challenges (TD ${dim})` : "Eternity Challenges"),
|
||||
multValue: dim => {
|
||||
let allMult = DC.D1.timesEffectsOf(
|
||||
EternityChallenge(1).reward,
|
||||
EternityChallenge(10).reward,
|
||||
).times(EternityChallenge(7).isRunning ? Tickspeed.perSecond : DC.D1);
|
||||
if (EternityChallenge(9).isRunning) {
|
||||
allMult = allMult.times(
|
||||
Decimal.pow(Math.clampMin(Currency.infinityPower.value.pow(InfinityDimensions.powerConversionRate / 7)
|
||||
.log2(), 1), 4).clampMin(1));
|
||||
}
|
||||
return Decimal.pow(allMult, dim ? 1 : MultiplierTabHelper.activeDimCount("TD"));
|
||||
},
|
||||
isActive: () => EternityChallenge(1).completions > 0,
|
||||
icon: MultiplierTabIcons.CHALLENGE("eternity")
|
||||
},
|
||||
tickspeed: {
|
||||
name: () => "Tickspeed (EC7)",
|
||||
displayOverride: () => {
|
||||
const tickRate = Tickspeed.perSecond;
|
||||
const activeDims = MultiplierTabHelper.activeDimCount("TD");
|
||||
return `${format(tickRate, 2, 2)}/sec on ${formatInt(activeDims)} ${pluralize("Dimension", activeDims)}
|
||||
➜ ${formatX(tickRate.pow(activeDims), 2, 2)}`;
|
||||
},
|
||||
multValue: () => Tickspeed.perSecond.pow(MultiplierTabHelper.activeDimCount("TD")),
|
||||
isActive: () => EternityChallenge(7).isRunning,
|
||||
icon: MultiplierTabIcons.TICKSPEED,
|
||||
},
|
||||
dilationUpgrade: {
|
||||
name: "Dilation Upgrade (Based on Replicanti)",
|
||||
multValue: dim => {
|
||||
const mult = Replicanti.areUnlocked && Replicanti.amount.gt(1)
|
||||
? DilationUpgrade.tdMultReplicanti.effectValue
|
||||
: DC.D1;
|
||||
return Decimal.pow(mult, dim ? 1 : MultiplierTabHelper.activeDimCount("TD"));
|
||||
},
|
||||
isActive: () => DilationUpgrade.tdMultReplicanti.canBeApplied,
|
||||
icon: MultiplierTabIcons.UPGRADE("dilation"),
|
||||
},
|
||||
realityUpgrade: {
|
||||
name: "Temporal Transcendence",
|
||||
multValue: dim => Decimal.pow(RealityUpgrade(22).effectOrDefault(1),
|
||||
dim ? 1 : MultiplierTabHelper.activeDimCount("TD")),
|
||||
isActive: () => RealityUpgrade(22).canBeApplied,
|
||||
icon: MultiplierTabIcons.UPGRADE("reality"),
|
||||
},
|
||||
glyph: {
|
||||
name: "Glyph Effects",
|
||||
powValue: () => getAdjustedGlyphEffect("timepow") * getAdjustedGlyphEffect("effarigdimensions"),
|
||||
isActive: () => PlayerProgress.realityUnlocked(),
|
||||
icon: MultiplierTabIcons.GENERIC_GLYPH
|
||||
},
|
||||
alchemy: {
|
||||
name: "Glyph Alchemy",
|
||||
multValue: dim => Decimal.pow(AlchemyResource.dimensionality.effectOrDefault(1),
|
||||
dim ? 1 : MultiplierTabHelper.activeDimCount("TD")),
|
||||
powValue: () => AlchemyResource.time.effectOrDefault(1) * Ra.momentumValue,
|
||||
isActive: () => Ra.unlocks.unlockGlyphAlchemy.canBeApplied,
|
||||
icon: MultiplierTabIcons.ALCHEMY,
|
||||
},
|
||||
imaginaryUpgrade: {
|
||||
name: "Suspicion of Interference",
|
||||
powValue: () => ImaginaryUpgrade(11).effectOrDefault(1),
|
||||
isActive: () => ImaginaryUpgrade(11).canBeApplied,
|
||||
icon: MultiplierTabIcons.UPGRADE("imaginary"),
|
||||
},
|
||||
pelle: {
|
||||
name: "Pelle Rift Effects",
|
||||
multValue: dim => Decimal.pow(PelleRifts.chaos.effectOrDefault(1),
|
||||
dim ? 1 : MultiplierTabHelper.activeDimCount("TD")),
|
||||
powValue: () => PelleRifts.paradox.effectOrDefault(DC.D1).toNumber(),
|
||||
isActive: () => Pelle.isDoomed,
|
||||
icon: MultiplierTabIcons.PELLE,
|
||||
},
|
||||
iap: {
|
||||
name: "Shop Tab Purchases",
|
||||
multValue: dim => Decimal.pow(ShopPurchase.allDimPurchases.currentMult,
|
||||
dim ? 1 : MultiplierTabHelper.activeDimCount("TD")),
|
||||
isActive: () => ShopPurchaseData.totalSTD > 0,
|
||||
icon: MultiplierTabIcons.IAP,
|
||||
},
|
||||
|
||||
nerfV: {
|
||||
name: "V's Reality",
|
||||
powValue: () => 0.5,
|
||||
isActive: () => V.isRunning,
|
||||
icon: MultiplierTabIcons.GENERIC_V,
|
||||
},
|
||||
nerfCursed: {
|
||||
name: "Cursed Glyphs",
|
||||
powValue: () => getAdjustedGlyphEffect("curseddimensions"),
|
||||
isActive: () => getAdjustedGlyphEffect("curseddimensions") !== 1,
|
||||
icon: MultiplierTabIcons.SPECIFIC_GLYPH("cursed"),
|
||||
},
|
||||
};
|
232
javascripts/core/secret-formula/multiplier-tab/tree.js
Normal file
232
javascripts/core/secret-formula/multiplier-tab/tree.js
Normal file
@ -0,0 +1,232 @@
|
||||
/* eslint-disable max-depth */
|
||||
/* eslint-disable camelcase */
|
||||
import { GameDatabase } from "../game-database";
|
||||
|
||||
import { MultiplierTabHelper } from "./helper-functions";
|
||||
|
||||
const dynamicGenProps = ["TP", "DT", "infinities", "eternities", "gamespeed"];
|
||||
const propList = {
|
||||
AD: ["purchase", "dimboost", "sacrifice", "achievementMult", "achievement", "infinityUpgrade",
|
||||
"breakInfinityUpgrade", "infinityPower", "infinityChallenge", "timeStudy", "eternityChallenge", "glyph", "v",
|
||||
"alchemy", "pelle", "iap", "effectNC", "nerfIC", "nerfV", "nerfCursed", "nerfPelle"],
|
||||
ID: ["purchase", "achievementMult", "achievement", "replicanti", "infinityChallenge", "timeStudy", "eternityUpgrade",
|
||||
"eternityChallenge", "glyph", "alchemy", "imaginaryUpgrade", "pelle", "iap", "nerfV", "nerfCursed", "nerfPelle"],
|
||||
TD: ["purchase", "achievementMult", "achievement", "timeStudy", "eternityUpgrade", "eternityChallenge",
|
||||
"dilationUpgrade", "realityUpgrade", "glyph", "alchemy", "imaginaryUpgrade", "pelle", "iap", "nerfV", "nerfCursed"],
|
||||
IP: ["base", "infinityUpgrade", "achievement", "timeStudy", "dilationUpgrade", "glyph", "alchemy", "pelle", "iap",
|
||||
"nerfTeresa", "nerfV"],
|
||||
EP: ["base", "eternityUpgrade", "timeStudy", "glyph", "realityUpgrade", "pelle", "iap", "nerfTeresa", "nerfV"],
|
||||
};
|
||||
|
||||
// Some of the props above would contain every entry except "total" in their respective value GameDB entry, so we
|
||||
// generate them dynamically instead
|
||||
for (const prop of dynamicGenProps) {
|
||||
propList[prop] = [];
|
||||
for (const toCopy of Object.keys(GameDatabase.multiplierTabValues[prop])) {
|
||||
if (toCopy !== "total") propList[prop].push(toCopy);
|
||||
}
|
||||
}
|
||||
|
||||
// Used for individual dimension breakdowns of effects (eg. full achievement mult into its values on individual ADs)
|
||||
// Results in an array of ["key_1", "key_2", ... , "key_8"]
|
||||
function append8(key) {
|
||||
const props = [];
|
||||
for (let dim = 1; dim <= 8; dim++) props.push(`${key}_${dim}`);
|
||||
return props;
|
||||
}
|
||||
|
||||
// Helper method to create very long lists of entries in the tree; format is "RESOURCE_SOURCE_DIMENSION"
|
||||
function getProps(resource, tier) {
|
||||
const props = propList[resource].map(s => `${resource}_${s}`);
|
||||
if (!tier) return props;
|
||||
const newProps = [];
|
||||
for (const effect of props) newProps.push(`${effect}_${tier}`);
|
||||
return newProps;
|
||||
}
|
||||
|
||||
// Everything is multiplierTabTree is associated with values in GameDatabase.multiplierTabValues. The only explicitly
|
||||
// initialized props here are the "root" props which are viewable on the tab with full breakdowns. After the initial
|
||||
// specification, all children props are dynamically added based on the arrays in the helper functions above
|
||||
GameDatabase.multiplierTabTree = {
|
||||
AM_total: [
|
||||
["AD_total", "tickspeed_total", "AM_effarigAM"]
|
||||
],
|
||||
AD_total: [
|
||||
append8("AD_total"),
|
||||
getProps("AD")
|
||||
],
|
||||
ID_total: [
|
||||
append8("ID_total"),
|
||||
getProps("ID")
|
||||
],
|
||||
TD_total: [
|
||||
append8("TD_total"),
|
||||
getProps("TD")
|
||||
],
|
||||
IP_total: [
|
||||
getProps("IP")
|
||||
],
|
||||
IP_base: [
|
||||
["IP_antimatter", "IP_divisor"]
|
||||
],
|
||||
EP_total: [
|
||||
getProps("EP")
|
||||
],
|
||||
EP_base: [
|
||||
["EP_IP", "EP_divisor"]
|
||||
],
|
||||
TP_total: [
|
||||
getProps("TP")
|
||||
],
|
||||
DT_total: [
|
||||
getProps("DT")
|
||||
],
|
||||
tickspeed_total: [
|
||||
["tickspeed_base", "tickspeed_upgrades", "tickspeed_galaxies"]
|
||||
],
|
||||
tickspeed_upgrades: [
|
||||
["tickspeedUpgrades_purchased", "tickspeedUpgrades_free"]
|
||||
],
|
||||
tickspeed_galaxies: [
|
||||
["galaxies_antimatter", "galaxies_replicanti", "galaxies_tachyon"]
|
||||
],
|
||||
infinities_total: [
|
||||
getProps("infinities")
|
||||
],
|
||||
eternities_total: [
|
||||
getProps("eternities")
|
||||
],
|
||||
gamespeed_total: [
|
||||
getProps("gamespeed")
|
||||
],
|
||||
};
|
||||
|
||||
// Gamespeed's two alternate displays are current and average gamespeed, distinguished by which of two
|
||||
// mutually-exclusive entries appear in the list. We explicity modify props here as needed
|
||||
const allGamespeed = GameDatabase.multiplierTabTree.gamespeed_total[0];
|
||||
GameDatabase.multiplierTabTree.gamespeed_total[0] = [...allGamespeed].filter(key => key !== "gamespeed_blackHoleAvg");
|
||||
GameDatabase.multiplierTabTree.gamespeed_total[1] = [...allGamespeed].filter(key => key !== "gamespeed_blackHoleCurr");
|
||||
|
||||
// DT doesn't explicitly have an entry to TP, due to it being its own total entry, so we link them together
|
||||
GameDatabase.multiplierTabTree.DT_total[0].unshift("TP_total");
|
||||
|
||||
// Additional data specification for dynamically-generated props
|
||||
const dimTypes = ["AD", "ID", "TD"];
|
||||
const singleRes = ["IP", "EP", "DT"];
|
||||
const targetedEffects = {
|
||||
achievement: {
|
||||
checkFn: MultiplierTabHelper.achievementDimCheck,
|
||||
AD: [23, 28, 31, 34, 43, 48, 56, 64, 65, 68, 71, 72, 73, 74, 76, 84, 91, 92],
|
||||
TD: [105, 128],
|
||||
IP: [85, 93, 116, 125, 141],
|
||||
DT: [132, 137]
|
||||
},
|
||||
timeStudy: {
|
||||
checkFn: MultiplierTabHelper.timeStudyDimCheck,
|
||||
AD: [71, 91, 101, 161, 193, 214, 234],
|
||||
ID: [72, 82, 92, 102, 162],
|
||||
TD: [11, 73, 93, 103, 151, 221, 227, 301],
|
||||
IP: [41, 51, 141, 142, 143],
|
||||
EP: [61, 121, 122, 123],
|
||||
},
|
||||
infinityChallenge: {
|
||||
checkFn: MultiplierTabHelper.ICDimCheck,
|
||||
AD: [3, 4, 8],
|
||||
ID: [1, 6],
|
||||
},
|
||||
eternityChallenge: {
|
||||
checkFn: MultiplierTabHelper.ECDimCheck,
|
||||
ID: [2, 4, 7, 9],
|
||||
TD: [1, 10],
|
||||
},
|
||||
};
|
||||
|
||||
// Highest actively-producing dimensions need a special case
|
||||
for (const dim of dimTypes) {
|
||||
GameDatabase.multiplierTabTree[`${dim}_total`][0].push(`${dim}_highestDim`);
|
||||
GameDatabase.multiplierTabTree[`${dim}_total`][1].push(`${dim}_highestDim`);
|
||||
}
|
||||
|
||||
// EC7 also needs a special case for tickspeed, since it doesn't appear on the multipliers themselves
|
||||
for (const dim of ["ID", "TD"]) {
|
||||
GameDatabase.multiplierTabTree[`${dim}_total`][0].push(`${dim}_tickspeed`);
|
||||
GameDatabase.multiplierTabTree[`${dim}_total`][1].push(`${dim}_tickspeed`);
|
||||
}
|
||||
|
||||
// Dynamically generate all values from existing values, but broken down by dimension
|
||||
for (const res of dimTypes) {
|
||||
for (const prop of getProps(res)) GameDatabase.multiplierTabTree[prop] = [append8(prop)];
|
||||
for (let dim = 1; dim <= 8; dim++) GameDatabase.multiplierTabTree[`${res}_total_${dim}`] = [getProps(res, dim)];
|
||||
}
|
||||
|
||||
// A few dynamically-generated props are largely useless in terms of what they connect to, in that they have very few
|
||||
// entries or have 8 identical entries, so we explicitly remove those lists for a cleaner appearance on the UI
|
||||
const removedRegexes = ["AD_sacrifice", "AD_breakInfinityUpgrade", "AD_nerfIC", "AD_infinityUpgrade", "AD_v",
|
||||
"ID_replicanti", "ID_infinityChallenge", "ID_eternityUpgrades",
|
||||
"TD_achievement", "TD_eternityUpgrade", "TD_dilationUpgrade", "TD_realityUpgrade",
|
||||
".._achievementMult", ".._glyph", ".._alchemy", ".._imaginaryUpgrade", ".._iap",
|
||||
".._nerfV", ".._nerfCursed", ".._nerfPelle", ".._pelle"
|
||||
];
|
||||
const removedProps = Object.keys(GameDatabase.multiplierTabTree)
|
||||
.filter(key => removedRegexes.some(regex => key.match(regex)));
|
||||
for (const prop of removedProps) {
|
||||
GameDatabase.multiplierTabTree[prop] = undefined;
|
||||
}
|
||||
|
||||
// We need to handle infinity power multiplier a bit differently; previous steps of dynamic generation fill it with
|
||||
// 8 identical AD multipliers, but we want to replace it with ID mults and the conversion rate
|
||||
GameDatabase.multiplierTabTree.AD_infinityPower = [["ID_total", "ID_powerConversion"]];
|
||||
for (let dim = 1; dim <= 8; dim++) {
|
||||
GameDatabase.multiplierTabTree[`AD_infinityPower_${dim}`] = [["ID_total", "ID_powerConversion"]];
|
||||
}
|
||||
|
||||
// Tesseracts are added one layer deep, but we don't want to override the existing ID_purchase entry
|
||||
GameDatabase.multiplierTabTree.ID_purchase.push(["ID_basePurchase", "ID_tesseractPurchase", "ID_infinityGlyphSacrifice",
|
||||
"ID_powPurchase"]);
|
||||
for (let dim = 1; dim <= 7; dim++) {
|
||||
GameDatabase.multiplierTabTree[`ID_purchase_${dim}`] = [[`ID_basePurchase_${dim}`, `ID_tesseractPurchase_${dim}`,
|
||||
"ID_powPurchase"]];
|
||||
}
|
||||
GameDatabase.multiplierTabTree.ID_purchase_8 = [[`ID_basePurchase_8`, `ID_infinityGlyphSacrifice`, "ID_powPurchase"]];
|
||||
|
||||
// These are also added one layer deep
|
||||
GameDatabase.multiplierTabTree.TD_purchase.push(["TD_basePurchase", "TD_timeGlyphSacrifice", "TD_powPurchase"]);
|
||||
GameDatabase.multiplierTabTree.TD_purchase_8 = [["TD_basePurchase_8", "TD_timeGlyphSacrifice", "TD_powPurchase"]];
|
||||
|
||||
// Dynamically fill effects which only affect certain dimensions, as noted in targetedEffects
|
||||
for (const res of dimTypes) {
|
||||
for (const eff of Object.keys(targetedEffects)) {
|
||||
if (!targetedEffects[eff][res]) continue;
|
||||
GameDatabase.multiplierTabTree[`${res}_${eff}`] = [[]];
|
||||
for (const id of targetedEffects[eff][res]) {
|
||||
for (let dim = 1; dim <= 8; dim++) {
|
||||
const propStr = `${res}_${eff}_${dim}`;
|
||||
const dimStr = `${res}${dim}`;
|
||||
if (targetedEffects[eff].checkFn(id, dimStr)) {
|
||||
if (!GameDatabase.multiplierTabTree[propStr]) GameDatabase.multiplierTabTree[propStr] = [[]];
|
||||
GameDatabase.multiplierTabTree[propStr][0].push(`general_${eff}_${id}_${dimStr}`);
|
||||
}
|
||||
}
|
||||
GameDatabase.multiplierTabTree[`${res}_${eff}`][0].push(`general_${eff}_${id}_${res}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamically fill effects which affect single resources as well
|
||||
for (const res of singleRes) {
|
||||
for (const eff of Object.keys(targetedEffects)) {
|
||||
if (!targetedEffects[eff][res]) continue;
|
||||
GameDatabase.multiplierTabTree[`${res}_${eff}`] = [[]];
|
||||
for (const ach of targetedEffects[eff][res]) {
|
||||
GameDatabase.multiplierTabTree[`${res}_${eff}`][0].push(`general_${eff}_${ach}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in eternity upgrade entries
|
||||
GameDatabase.multiplierTabTree.ID_eternityUpgrade = [[`ID_eu1`, `ID_eu2`, `ID_eu3`]];
|
||||
GameDatabase.multiplierTabTree.TD_eternityUpgrade = [[`TD_eu1`, `TD_eu2`]];
|
||||
for (let dim = 1; dim <= 8; dim++) {
|
||||
GameDatabase.multiplierTabTree[`ID_eternityUpgrade_${dim}`] = [[`ID_eu1_${dim}`, `ID_eu2_${dim}`, `ID_eu3_${dim}`]];
|
||||
GameDatabase.multiplierTabTree[`TD_eternityUpgrade_${dim}`] = [[`TD_eu1_${dim}`, `TD_eu2_${dim}`]];
|
||||
}
|
@ -296,8 +296,8 @@ GameDatabase.reality.imaginaryUpgrades = [
|
||||
at least ${formatInt(4)} empty Glyph slots`,
|
||||
hasFailed: () => !Laitela.isRunning || Laitela.maxAllowedDimension !== 0 || Glyphs.activeList.length > 1,
|
||||
checkRequirement: () => Laitela.isRunning && Laitela.maxAllowedDimension === 0 &&
|
||||
Glyphs.activeList.length <= 1,
|
||||
checkEvent: GAME_EVENT.REALITY_RESET_BEFORE,
|
||||
Glyphs.activeList.length <= 1 && TimeStudy.reality.isBought,
|
||||
checkEvent: GAME_EVENT.GAME_TICK_AFTER,
|
||||
description: "Unlock Pelle, Celestial of Antimatter",
|
||||
},
|
||||
];
|
||||
|
@ -274,12 +274,12 @@ GameDatabase.reality.upgrades = [
|
||||
name: "Parity of Singularity",
|
||||
id: 20,
|
||||
cost: 1500,
|
||||
requirement: () => `${formatInt(1)} year total play time and the Black Hole unlocked
|
||||
(Currently: ${Time.totalTimePlayed.toStringShort(false)})`,
|
||||
requirement: () => `${formatInt(100)} days total play time after unlocking the Black Hole
|
||||
(Currently: ${Time.timeSinceBlackHole.toStringShort(false)})`,
|
||||
hasFailed: () => !BlackHole(1).isUnlocked && Currency.realityMachines.lt(100),
|
||||
checkRequirement: () => Time.totalTimePlayed.totalYears >= 1 && BlackHole(1).isUnlocked,
|
||||
checkRequirement: () => Time.timeSinceBlackHole.totalDays >= 100 && BlackHole(1).isUnlocked,
|
||||
checkEvent: GAME_EVENT.GAME_TICK_AFTER,
|
||||
description: "Unlock Black Hole 2",
|
||||
description: "Unlock another Black Hole",
|
||||
automatorPoints: 10,
|
||||
shortDescription: () => `Second Black Hole`,
|
||||
formatCost: value => format(value, 1, 0)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { GameDatabase } from "./game-database";
|
||||
|
||||
// NOTE: IF ANY COSTS ARE CHANGED HERE, THEY ALSO NEED TO BE CHANGED ON THE BACKEND TOO
|
||||
GameDatabase.shopPurchases = {
|
||||
dimPurchases: {
|
||||
key: "dimPurchases",
|
||||
@ -52,7 +53,7 @@ GameDatabase.shopPurchases = {
|
||||
description: "Get 6 hours worth of offline production. (Autobuyers don't work at full speed)",
|
||||
singleUse: true,
|
||||
onPurchase: () => {
|
||||
kong.purchaseTimeSkip();
|
||||
shop.purchaseTimeSkip();
|
||||
}
|
||||
},
|
||||
bigTimeSkip: {
|
||||
@ -61,7 +62,7 @@ GameDatabase.shopPurchases = {
|
||||
description: "Get 24 hours worth of offline production. (Autobuyers don't work at full speed)",
|
||||
singleUse: true,
|
||||
onPurchase: () => {
|
||||
kong.purchaseLongerTimeSkip();
|
||||
shop.purchaseLongerTimeSkip();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -171,6 +171,39 @@ GameDatabase.tabNotifications = {
|
||||
tab: "autobuyers"
|
||||
},
|
||||
],
|
||||
// Always externally triggered, but needs to be ignored in cel7 because they're unlocked differently
|
||||
condition: () => !Pelle.isDoomed,
|
||||
},
|
||||
imaginaryMachineUnlock: {
|
||||
id: 13,
|
||||
tabsToHighLight: [
|
||||
{
|
||||
parent: "reality",
|
||||
tab: "imag_upgrades"
|
||||
}
|
||||
],
|
||||
condition: () => MachineHandler.isIMUnlocked,
|
||||
events: [GAME_EVENT.GAME_TICK_AFTER]
|
||||
},
|
||||
laitelaUnlock: {
|
||||
id: 14,
|
||||
tabsToHighLight: [
|
||||
{
|
||||
parent: "celestials",
|
||||
tab: "laitela"
|
||||
},
|
||||
],
|
||||
// Always externally triggered
|
||||
condition: () => true,
|
||||
},
|
||||
pelleUnlock: {
|
||||
id: 15,
|
||||
tabsToHighLight: [
|
||||
{
|
||||
parent: "celestials",
|
||||
tab: "pelle"
|
||||
},
|
||||
],
|
||||
// Always externally triggered
|
||||
condition: () => true,
|
||||
},
|
||||
|
@ -110,13 +110,22 @@ GameDatabase.tabs = [
|
||||
id: 2,
|
||||
hidable: true,
|
||||
},
|
||||
{
|
||||
key: "multipliers",
|
||||
name: "Multiplier Breakdown",
|
||||
symbol: "<i class='fas fa-calculator'></i>",
|
||||
component: "MultiplierBreakdownTab",
|
||||
condition: () => PlayerProgress.infinityUnlocked(),
|
||||
id: 3,
|
||||
hidable: true,
|
||||
},
|
||||
{
|
||||
key: "glyph sets",
|
||||
name: "Glyph Set Records",
|
||||
symbol: "<i class='fas fa-ellipsis-h'></i>",
|
||||
component: "GlyphSetRecordsTab",
|
||||
condition: () => PlayerProgress.realityUnlocked(),
|
||||
id: 3,
|
||||
id: 4,
|
||||
hidable: true,
|
||||
},
|
||||
{
|
||||
@ -125,7 +134,7 @@ GameDatabase.tabs = [
|
||||
symbol: "<i class='fas fa-flag-checkered'></i>",
|
||||
component: "SpeedrunMilestonesTab",
|
||||
condition: () => player.speedrun.isActive,
|
||||
id: 4,
|
||||
id: 5,
|
||||
hidable: true,
|
||||
},
|
||||
]
|
||||
@ -206,7 +215,7 @@ GameDatabase.tabs = [
|
||||
name: "Infinity Challenges",
|
||||
symbol: "∞",
|
||||
component: "infinity-challenges-tab",
|
||||
condition: () => PlayerProgress.hasBroken() || Pelle.isDoomed,
|
||||
condition: () => PlayerProgress.realityUnlocked() || PlayerProgress.hasBroken() || Pelle.isDoomed,
|
||||
id: 1,
|
||||
hidable: true
|
||||
},
|
||||
@ -467,7 +476,6 @@ GameDatabase.tabs = [
|
||||
name: "Shop",
|
||||
newUIClass: "shop",
|
||||
hideAt: 1.5,
|
||||
condition: () => 1===1 /*kong.enabled || player.IAP.totalSTD > 0, ||*/,
|
||||
id: 10,
|
||||
hidable: true,
|
||||
subtabs: [
|
||||
|
225
javascripts/core/shop.js
Normal file
225
javascripts/core/shop.js
Normal file
@ -0,0 +1,225 @@
|
||||
import { RebuyableMechanicState } from "./game-mechanics/index";
|
||||
|
||||
import Payments from "./payments";
|
||||
|
||||
export const shop = {};
|
||||
|
||||
shop.kongEnabled = false;
|
||||
|
||||
shop.init = function() {
|
||||
if (document.referrer.indexOf("kongregate") === -1)
|
||||
return;
|
||||
shop.kongEnabled = true;
|
||||
try {
|
||||
kongregateAPI.loadAPI(() => {
|
||||
window.kongregate = kongregateAPI.getAPI();
|
||||
});
|
||||
// eslint-disable-next-line no-console
|
||||
} catch (err) { console.log("Couldn't load Kongregate API"); }
|
||||
};
|
||||
|
||||
export const ShopPurchaseData = {
|
||||
totalSTD: 0,
|
||||
spentSTD: 0,
|
||||
respecAvailable: false,
|
||||
|
||||
get availableSTD() {
|
||||
return this.totalSTD - this.spentSTD;
|
||||
},
|
||||
|
||||
get isIAPEnabled() {
|
||||
return Cloud.loggedIn && this.availableSTD >= 0 && player.IAP.enabled;
|
||||
},
|
||||
|
||||
updateLocalSTD(newData) {
|
||||
this.totalSTD = newData.totalSTD;
|
||||
this.spentSTD = newData.spentSTD;
|
||||
this.respecAvailable = newData.respecAvailable;
|
||||
for (const key of Object.keys(GameDatabase.shopPurchases)) this[key] = newData[key] ?? 0;
|
||||
GameStorage.save();
|
||||
},
|
||||
|
||||
// Reads STD props from the cloud and sets local cached values with the result
|
||||
async syncSTD(showNotification = true, fetchedData = undefined) {
|
||||
if (!Cloud.loggedIn) return;
|
||||
let newSTDData;
|
||||
if (fetchedData) {
|
||||
newSTDData = fetchedData;
|
||||
} else {
|
||||
try {
|
||||
const statusRes = await fetch(`${STD_BACKEND_URL}/STDData?user=${Cloud.user.id}`);
|
||||
newSTDData = await statusRes.json();
|
||||
} catch (e) {
|
||||
GameUI.notify.error("Could not sync STD purchases!", 10000);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (showNotification) GameUI.notify.info("STD purchases successfully loaded!", 10000);
|
||||
this.updateLocalSTD(newSTDData);
|
||||
},
|
||||
|
||||
respecRequest() {
|
||||
if (!Cloud.loggedIn) {
|
||||
Modal.message.show(`You are not logged in to Google, anything on this tab cannot be used unless you log in.`);
|
||||
} else if (player.options.confirmations.respecIAP) {
|
||||
Modal.respecIAP.show();
|
||||
} else {
|
||||
this.respecAll();
|
||||
}
|
||||
},
|
||||
|
||||
async respecAll() {
|
||||
if (!this.respecAvailable) {
|
||||
Modal.message.show(`You do not have a respec available. Making an STD purchase allows you to respec your upgrades
|
||||
once. You can only have at most one of these respecs, and they do not refund offline production purchases.`);
|
||||
return;
|
||||
}
|
||||
let res;
|
||||
try {
|
||||
res = await fetch(`${STD_BACKEND_URL}/respec`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({ user: Cloud.user.id })
|
||||
});
|
||||
} catch (e) {
|
||||
GameUI.notify.error("Unable to respec STD purchases!", 10000);
|
||||
return;
|
||||
}
|
||||
const stdData = await res.json();
|
||||
if (stdData.success) GameUI.notify.info("STD respec successful!", 10000);
|
||||
else GameUI.notify.error("No purchases to respec!", 10000);
|
||||
this.updateLocalSTD(stdData.data);
|
||||
},
|
||||
};
|
||||
|
||||
// We track the local state of shop purchases here, so dynamically add all the keys which exist in the gameDB
|
||||
for (const key of Object.keys(GameDatabase.shopPurchases)) ShopPurchaseData[key] = 0;
|
||||
|
||||
class ShopPurchaseState extends RebuyableMechanicState {
|
||||
get currency() {
|
||||
return ShopPurchaseData.availableSTD;
|
||||
}
|
||||
|
||||
get isAffordable() {
|
||||
return this.currency >= this.cost;
|
||||
}
|
||||
|
||||
get description() {
|
||||
return this.config.description;
|
||||
}
|
||||
|
||||
get cost() {
|
||||
return this.config.cost;
|
||||
}
|
||||
|
||||
get purchases() {
|
||||
return ShopPurchaseData[this.config.key];
|
||||
}
|
||||
|
||||
set purchases(value) {
|
||||
ShopPurchaseData[this.config.key] = value;
|
||||
}
|
||||
|
||||
get shouldDisplayMult() {
|
||||
return Boolean(this.config.multiplier);
|
||||
}
|
||||
|
||||
get currentMult() {
|
||||
if (!this.shouldDisplayMult) return "";
|
||||
return this.config.multiplier(ShopPurchaseData.isIAPEnabled ? this.purchases : 0);
|
||||
}
|
||||
|
||||
get nextMult() {
|
||||
if (!this.shouldDisplayMult) return "";
|
||||
return this.config.multiplier(ShopPurchaseData.isIAPEnabled ? this.purchases + 1 : 0);
|
||||
}
|
||||
|
||||
// We want to still display the correct value in the button, so we need separate getters for it
|
||||
get currentMultForDisplay() {
|
||||
if (!this.shouldDisplayMult) return "";
|
||||
return this.config.multiplier(this.purchases);
|
||||
}
|
||||
|
||||
get nextMultForDisplay() {
|
||||
if (!this.shouldDisplayMult) return "";
|
||||
return this.config.multiplier(this.purchases + 1);
|
||||
}
|
||||
|
||||
formatEffect(effect) {
|
||||
return this.config.formatEffect?.(effect) || formatX(effect, 2, 0);
|
||||
}
|
||||
|
||||
async purchase() {
|
||||
if (!this.canBeBought) return false;
|
||||
if (GameEnd.creditsEverClosed) return false;
|
||||
if (this.config.singleUse && ui.$viewModel.modal.progressBar) return false;
|
||||
|
||||
// Contact the firebase server to verify the purchase
|
||||
const success = await Payments.buyUpgrade(this.config.key);
|
||||
if (!success) return false;
|
||||
|
||||
if (player.IAP.enabled) Speedrun.setSTDUse(true);
|
||||
if (this.config.singleUse) this.config.onPurchase();
|
||||
GameUI.update();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export const ShopPurchase = mapGameDataToObject(
|
||||
GameDatabase.shopPurchases,
|
||||
config => new ShopPurchaseState(config)
|
||||
);
|
||||
|
||||
shop.purchaseTimeSkip = function() {
|
||||
Speedrun.setSTDUse(true);
|
||||
simulateTime(3600 * 6);
|
||||
};
|
||||
|
||||
shop.purchaseLongerTimeSkip = function() {
|
||||
Speedrun.setSTDUse(true);
|
||||
simulateTime(3600 * 24);
|
||||
};
|
||||
|
||||
shop.migratePurchases = function() {
|
||||
if (!shop.kongEnabled) return;
|
||||
try {
|
||||
kongregate.mtx.requestUserItemList("", items);
|
||||
// eslint-disable-next-line no-console
|
||||
} catch (e) { console.log(e); }
|
||||
|
||||
function items(result) {
|
||||
let ipPurchases = 0;
|
||||
let dimPurchases = 0;
|
||||
let epPurchases = 0;
|
||||
let alldimPurchases = 0;
|
||||
for (const item of result.data) {
|
||||
if (item.identifier === "doublemult") {
|
||||
player.IAP.totalSTD += 30;
|
||||
player.IAP.spentSTD += 30;
|
||||
dimPurchases++;
|
||||
}
|
||||
if (item.identifier === "doubleip") {
|
||||
player.IAP.totalSTD += 40;
|
||||
player.IAP.spentSTD += 40;
|
||||
ipPurchases++;
|
||||
}
|
||||
if (item.identifier === "tripleep") {
|
||||
player.IAP.totalSTD += 50;
|
||||
player.IAP.spentSTD += 50;
|
||||
epPurchases++;
|
||||
}
|
||||
if (item.identifier === "alldimboost") {
|
||||
player.IAP.totalSTD += 60;
|
||||
player.IAP.spentSTD += 60;
|
||||
alldimPurchases++;
|
||||
}
|
||||
|
||||
}
|
||||
player.IAP.dimPurchases = dimPurchases;
|
||||
player.IAP.allDimPurchases = alldimPurchases;
|
||||
player.IAP.IPPurchases = ipPurchases;
|
||||
player.IAP.EPPurchases = epPurchases;
|
||||
}
|
||||
};
|
@ -49,6 +49,14 @@ export const Speedrun = {
|
||||
player.speedrun.hasStarted = true;
|
||||
player.speedrun.startDate = Date.now();
|
||||
player.lastUpdate = Date.now();
|
||||
|
||||
// This needs to be calculated "live" because using spentSTD includes any offline progress purchases too
|
||||
let currentSpent = 0;
|
||||
for (const purchase of ShopPurchase.all) {
|
||||
if (purchase.config.singleUse) continue;
|
||||
currentSpent += purchase.purchases * purchase.cost;
|
||||
}
|
||||
this.setSTDUse(ShopPurchaseData.isIAPEnabled && currentSpent > 0);
|
||||
},
|
||||
isPausedAtStart() {
|
||||
return player.speedrun.isActive && !player.speedrun.hasStarted;
|
||||
@ -59,7 +67,11 @@ export const Speedrun = {
|
||||
setSegmented(state) {
|
||||
if (this.isPausedAtStart()) return;
|
||||
player.speedrun.isSegmented = state;
|
||||
}
|
||||
},
|
||||
setSTDUse(state) {
|
||||
if (this.isPausedAtStart() || ShopPurchaseData.spentSTD === 0) return;
|
||||
player.speedrun.usedSTD = state;
|
||||
},
|
||||
};
|
||||
|
||||
class SpeedrunMilestone extends GameMechanicState {
|
||||
|
@ -29,13 +29,9 @@ export const Cloud = {
|
||||
auth: getAuth(),
|
||||
db: getDatabase(),
|
||||
user: null,
|
||||
hasSeenSavingConflict: false,
|
||||
shouldOverwriteCloudSave: true,
|
||||
lastCloudHash: null,
|
||||
|
||||
resetTempState() {
|
||||
this.hasSeenSavingConflict = false;
|
||||
this.shouldOverwriteCloudSave = true;
|
||||
this.lastCloudHash = null;
|
||||
GameStorage.lastCloudSave = Date.now();
|
||||
GameIntervals.checkCloudSave.restart();
|
||||
@ -46,7 +42,13 @@ export const Cloud = {
|
||||
},
|
||||
|
||||
async login() {
|
||||
await signInWithPopup(this.auth, this.provider);
|
||||
try {
|
||||
await signInWithPopup(this.auth, this.provider);
|
||||
ShopPurchaseData.syncSTD();
|
||||
GameUI.notify.success(`Logged in as ${this.user.displayName}`);
|
||||
} catch (e) {
|
||||
GameUI.notify.error("Google Account login failed");
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -84,13 +86,12 @@ export const Cloud = {
|
||||
return {
|
||||
farther: ProgressChecker.compareSaveProgress(cloud, local),
|
||||
older: ProgressChecker.compareSaveTimes(cloud, local),
|
||||
diffSTD: (cloud?.IAP?.totalSTD ?? 0) - (local?.IAP?.totalSTD ?? 0),
|
||||
differentName: cloud?.options.saveFileName !== local?.options.saveFileName,
|
||||
hashMismatch: this.lastCloudHash && this.lastCloudHash !== hash,
|
||||
};
|
||||
},
|
||||
|
||||
async saveCheck() {
|
||||
async saveCheck(forceModal = false) {
|
||||
const save = await this.load();
|
||||
if (save === null) {
|
||||
this.save();
|
||||
@ -99,6 +100,7 @@ export const Cloud = {
|
||||
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);
|
||||
|
||||
@ -112,11 +114,11 @@ export const Cloud = {
|
||||
const hasBoth = cloudSave && localSave;
|
||||
// NOTE THIS CHECK IS INTENTIONALLY DIFFERENT FROM THE LOAD CHECK
|
||||
const hasConflict = hasBoth && (saveComparison.older === -1 || saveComparison.farther !== 1 ||
|
||||
saveComparison.diffSTD > 0 || saveComparison.differentName || saveComparison.hashMismatch);
|
||||
if (hasConflict && !this.hasSeenSavingConflict) {
|
||||
saveComparison.differentName || saveComparison.hashMismatch);
|
||||
if (forceModal || (hasConflict && player.options.showCloudModal)) {
|
||||
Modal.addCloudConflict(saveId, saveComparison, cloudSave, localSave, overwriteAndSendCloudSave);
|
||||
Modal.cloudSaveConflict.show();
|
||||
} else if (!hasConflict || (this.hasSeenSavingConflict && this.shouldOverwriteCloudSave)) {
|
||||
} else if (!hasConflict || player.options.forceCloudOverwrite) {
|
||||
overwriteAndSendCloudSave();
|
||||
}
|
||||
}
|
||||
@ -158,7 +160,7 @@ export const Cloud = {
|
||||
// Bring up the modal if cloud loading will overwrite a local save which is older or possibly farther
|
||||
const hasBoth = cloudSave && localSave;
|
||||
const hasConflict = hasBoth && (saveComparison.older === 1 || saveComparison.farther !== -1 ||
|
||||
saveComparison.diffSTD < 0 || saveComparison.differentName);
|
||||
saveComparison.differentName);
|
||||
if (hasConflict) {
|
||||
Modal.addCloudConflict(saveId, saveComparison, cloudSave, localSave, overwriteLocalSave);
|
||||
Modal.cloudLoadConflict.show();
|
||||
@ -187,6 +189,7 @@ export const Cloud = {
|
||||
displayName: user.displayName,
|
||||
email: user.email,
|
||||
};
|
||||
ShopPurchaseData.syncSTD();
|
||||
} else {
|
||||
this.user = null;
|
||||
}
|
||||
|
@ -246,6 +246,19 @@ GameStorage.devMigrations = {
|
||||
// These are unused now
|
||||
delete player.celestials.effarig.typePriorityOrder;
|
||||
delete player.celestials.teresa.typePriorityOrder;
|
||||
// This property didn't even exist at the time of this change
|
||||
movePropIfPossible("teresa", "effarig", "glyphScoreSettings", {
|
||||
mode: AUTO_GLYPH_SCORE.LOWEST_SACRIFICE,
|
||||
simpleEffectCount: 0,
|
||||
types: GlyphTypes.list.mapToObject(t => t.id, t => ({
|
||||
rarityThreshold: 0,
|
||||
scoreThreshold: 0,
|
||||
effectCount: 0,
|
||||
effectChoices: t.effects.mapToObject(e => e.id, () => false),
|
||||
effectScores: t.effects.mapToObject(e => e.id, () => 0),
|
||||
})),
|
||||
});
|
||||
movePropIfPossible("effarig", "teresa", "bestAMSet", []);
|
||||
},
|
||||
player => {
|
||||
player.blackHole = player.wormhole;
|
||||
@ -453,15 +466,16 @@ GameStorage.devMigrations = {
|
||||
GameStorage.migrations.removeDimensionCosts,
|
||||
GameStorage.migrations.renameTickspeedPurchaseBumps,
|
||||
player => {
|
||||
player.celestials.teresa.unlockBits = arrayToBits(player.celestials.teresa.unlocks);
|
||||
const safeArrayToBits = x => ((x === undefined) ? 0 : arrayToBits(x));
|
||||
player.celestials.teresa.unlockBits = safeArrayToBits(player.celestials.teresa.unlocks);
|
||||
delete player.celestials.teresa.unlocks;
|
||||
player.celestials.effarig.unlockBits = arrayToBits(player.celestials.effarig.unlocks);
|
||||
player.celestials.effarig.unlockBits = safeArrayToBits(player.celestials.effarig.unlocks);
|
||||
delete player.celestials.effarig.unlocks;
|
||||
player.celestials.v.unlockBits = arrayToBits(player.celestials.v.unlocks);
|
||||
player.celestials.v.unlockBits = safeArrayToBits(player.celestials.v.unlocks);
|
||||
delete player.celestials.v.unlocks;
|
||||
player.celestials.ra.unlockBits = arrayToBits(player.celestials.ra.unlocks);
|
||||
player.celestials.ra.unlockBits = safeArrayToBits(player.celestials.ra.unlocks);
|
||||
delete player.celestials.ra.unlocks;
|
||||
player.celestials.laitela.unlockBits = arrayToBits(player.celestials.laitela.unlocks);
|
||||
player.celestials.laitela.unlockBits = safeArrayToBits(player.celestials.laitela.unlocks);
|
||||
delete player.celestials.laitela.unlocks;
|
||||
},
|
||||
player => {
|
||||
@ -713,21 +727,25 @@ GameStorage.devMigrations = {
|
||||
}
|
||||
},
|
||||
player => {
|
||||
for (let i = 0; i < player.dimensions.normal.length; i++) {
|
||||
const dimension = player.dimensions.normal[i];
|
||||
player.dimensions.antimatter[i].bought = dimension.bought;
|
||||
player.dimensions.antimatter[i].costBumps = dimension.costBumps;
|
||||
player.dimensions.antimatter[i].amount = new Decimal(dimension.amount);
|
||||
if (player.dimensions.normal !== undefined) {
|
||||
for (let i = 0; i < player.dimensions.normal.length; i++) {
|
||||
const dimension = player.dimensions.normal[i];
|
||||
player.dimensions.antimatter[i].bought = dimension.bought;
|
||||
player.dimensions.antimatter[i].costBumps = dimension.costBumps;
|
||||
player.dimensions.antimatter[i].amount = new Decimal(dimension.amount);
|
||||
}
|
||||
delete player.dimensions.normal;
|
||||
}
|
||||
delete player.dimensions.normal;
|
||||
},
|
||||
player => {
|
||||
player.options.news = {
|
||||
enabled: player.options.news,
|
||||
repeatBuffer: 40,
|
||||
AIChance: 0,
|
||||
speed: 1
|
||||
};
|
||||
if (player.options.news.enabled === undefined) {
|
||||
player.options.news = {
|
||||
enabled: player.options.news,
|
||||
repeatBuffer: 40,
|
||||
AIChance: 0,
|
||||
speed: 1
|
||||
};
|
||||
}
|
||||
},
|
||||
player => {
|
||||
delete player.options.confirmations.glyphTrash;
|
||||
@ -901,25 +919,42 @@ GameStorage.devMigrations = {
|
||||
delete player.dilation.freeGalaxies;
|
||||
},
|
||||
player => {
|
||||
player.auto.infinityDims = Array.range(0, 8).map(() => ({ lastTick: 0 }));
|
||||
for (let i = 0; i < 8; i++) {
|
||||
player.auto.infinityDims[i].isActive = player.infDimBuyers[i];
|
||||
}
|
||||
player.auto.timeDims = Array.range(0, 8).map(() => ({ lastTick: 0 }));
|
||||
for (let i = 0; i < 8; i++) {
|
||||
player.auto.timeDims[i].isActive = player.reality.tdbuyers[i];
|
||||
}
|
||||
player.auto.replicantiUpgrades = Array.range(0, 3).map(() => ({ lastTick: 0 }));
|
||||
for (let i = 0; i < 3; i++) {
|
||||
player.auto.replicantiUpgrades[i].isActive = player.replicanti.auto[i];
|
||||
}
|
||||
if (player.dilation.auto === undefined) {
|
||||
// Not defined on old saves, we define it only to delete it later in this migration
|
||||
player.dilation.auto = [true, true, true];
|
||||
}
|
||||
player.auto.dilationUpgrades = Array.range(0, 3).map(() => ({ lastTick: 0 }));
|
||||
for (let i = 0; i < 3; i++) {
|
||||
player.auto.dilationUpgrades[i].isActive = player.dilation.auto[i];
|
||||
}
|
||||
player.auto.blackHolePower = Array.range(0, 2).map(() => ({ lastTick: 0 }));
|
||||
for (let i = 0; i < 2; i++) {
|
||||
player.auto.blackHolePower[i].isActive = player.blackHole[i].autoPower;
|
||||
}
|
||||
if (player.reality.rebuyablesAuto === undefined) {
|
||||
// Not defined on old saves, we define it only to delete it later in this migration
|
||||
player.reality.rebuyablesAuto = [true, true, true, true, true];
|
||||
}
|
||||
player.auto.realityUpgrades = Array.range(0, 5).map(() => ({ lastTick: 0 }));
|
||||
for (let i = 0; i < 5; i++) {
|
||||
player.auto.realityUpgrades[i].isActive = player.reality.rebuyablesAuto[i];
|
||||
}
|
||||
player.auto.antimatterDims = player.auto.dimensions;
|
||||
// Note: player.autobuyers, the old way of storing autobuyers, seems to have gotten lost in dev migrations
|
||||
if (player.auto.antimatterDims === undefined) {
|
||||
player.auto.antimatterDims = player.auto.dimensions;
|
||||
}
|
||||
player.auto.replicantiGalaxies.isActive = player.replicanti.galaxybuyer;
|
||||
player.auto.ipMultBuyer.isActive = player.infMultBuyer;
|
||||
player.auto.epMultBuyer.isActive = player.reality.epmultbuyer;
|
||||
@ -1087,12 +1122,14 @@ GameStorage.devMigrations = {
|
||||
delete player.celestials.v.maxGlyphsThisRun;
|
||||
|
||||
// Refactor news storage format to bitmask array
|
||||
const oldNewsArray = player.news;
|
||||
delete player.news;
|
||||
player.news = {};
|
||||
player.news.seen = {};
|
||||
for (const id of oldNewsArray) NewsHandler.addSeenNews(id);
|
||||
player.news.totalSeen = NewsHandler.uniqueTickersSeen;
|
||||
if (Array.isArray(player.news)) {
|
||||
const oldNewsArray = player.news;
|
||||
delete player.news;
|
||||
player.news = {};
|
||||
player.news.seen = {};
|
||||
for (const id of oldNewsArray) NewsHandler.addSeenNews(id);
|
||||
player.news.totalSeen = NewsHandler.uniqueTickersSeen;
|
||||
}
|
||||
|
||||
// Separate news-specific data
|
||||
player.news.specialTickerData = {
|
||||
@ -1171,8 +1208,11 @@ GameStorage.devMigrations = {
|
||||
script.content = script.content.replaceAll(triadRegex, "30$1");
|
||||
}
|
||||
|
||||
player.timestudy.studies = player.timestudy.studies.concat(player.celestials.v.triadStudies.map(id => id + 300));
|
||||
delete player.celestials.v.triadStudies;
|
||||
if (player.celestials.v.triadStudies !== undefined) {
|
||||
player.timestudy.studies = player.timestudy.studies.concat(
|
||||
player.celestials.v.triadStudies.map(id => id + 300));
|
||||
delete player.celestials.v.triadStudies;
|
||||
}
|
||||
},
|
||||
player => {
|
||||
delete player.options.confirmations.harshAutoClean;
|
||||
@ -1391,6 +1431,10 @@ GameStorage.devMigrations = {
|
||||
const toMove = ["antimatterDims", "infinityDims", "timeDims", "replicantiUpgrades", "dilationUpgrades",
|
||||
"blackHolePower", "realityUpgrades", "imaginaryUpgrades"];
|
||||
for (const x of toMove) {
|
||||
if (player.auto[x].all !== undefined) {
|
||||
// Already up to date
|
||||
continue;
|
||||
}
|
||||
const all = player.auto[x];
|
||||
delete player.auto[x];
|
||||
player.auto[x] = { all, isActive: true };
|
||||
@ -1459,6 +1503,24 @@ GameStorage.devMigrations = {
|
||||
player.achievementBits[10] &= ~4;
|
||||
}
|
||||
},
|
||||
player => {
|
||||
if (player.options.newUI) {
|
||||
player.options.themeModern = player.options.theme ?? player.options.themeModern;
|
||||
} else {
|
||||
player.options.themeClassic = player.options.theme ?? player.options.themeClassic;
|
||||
}
|
||||
delete player.options.theme;
|
||||
|
||||
if (BlackHole(1).isUnlocked) player.records.timePlayedAtBHUnlock = player.records.totalTimePlayed;
|
||||
},
|
||||
player => {
|
||||
player.IAP.enabled = !player.IAP.disabled;
|
||||
const toDelete = ["totalSTD", "spentSTD", "exportSTD", "IPPurchases", "EPPurchases", "RMPurchases",
|
||||
"dimPurchases", "allDimPurchases", "replicantiPurchases", "dilatedTimePurchases", "disabled"];
|
||||
for (const key of toDelete) delete player.IAP[key];
|
||||
|
||||
// TODO Possibly update this with a dev STD migration?
|
||||
}
|
||||
],
|
||||
|
||||
patch(player) {
|
||||
|
@ -152,7 +152,7 @@ GameStorage.migrations = {
|
||||
GameStorage.migrations.moveTS33(player);
|
||||
GameStorage.migrations.addBestPrestigeCurrency(player);
|
||||
|
||||
kong.migratePurchases();
|
||||
shop.migratePurchases();
|
||||
}
|
||||
},
|
||||
|
||||
@ -374,7 +374,7 @@ GameStorage.migrations = {
|
||||
|
||||
adjustThemes(player) {
|
||||
delete player.options.themes;
|
||||
if (player.options.theme === undefined) player.options.theme = "Normal";
|
||||
if (player.options.theme === undefined) player.options.themeClassic = "Normal";
|
||||
delete player.options.secretThemeKey;
|
||||
},
|
||||
|
||||
@ -704,7 +704,7 @@ GameStorage.migrations = {
|
||||
// (2) a two-way swap of costco sells dimboosts now and 8 nobody got time for that
|
||||
// (3) a two-way swap of long lasting relationship and eternities are the new infinity
|
||||
const swaps = { "4,3": "6,4", "6,4": "7,7", "7,7": "4,3",
|
||||
"10,1": "11,7", "11,7": "10,1", "11,3": "12,4", "12,4": "11,3" };
|
||||
"10,1": "11,7", "11,7": "10,1", "11,3": "12,4", "12,4": "11,3" };
|
||||
const convertAchievementArray = (newAchievements, oldAchievements, isSecret) => {
|
||||
for (const oldId of oldAchievements) {
|
||||
let row = Math.floor(oldId / 10);
|
||||
@ -883,8 +883,9 @@ GameStorage.migrations = {
|
||||
},
|
||||
|
||||
removePriority(player) {
|
||||
const dims = player.auto.antimatterDims.all ?? player.auto.antimatterDims;
|
||||
for (let i = 0; i < 8; i++) {
|
||||
delete player.auto.antimatterDims[i].priority;
|
||||
delete dims[i].priority;
|
||||
}
|
||||
delete player.auto.tickspeed.priority;
|
||||
},
|
||||
|
@ -169,11 +169,7 @@ export const GameStorage = {
|
||||
},
|
||||
|
||||
export() {
|
||||
const segmented = player.speedrun.isSegmented;
|
||||
Speedrun.setSegmented(true);
|
||||
const save = GameSaveSerializer.serialize(player);
|
||||
Speedrun.setSegmented(segmented);
|
||||
copyToClipboard(save);
|
||||
copyToClipboard(this.exportModifiedSave());
|
||||
GameUI.notify.info("Exported current savefile to your clipboard");
|
||||
},
|
||||
|
||||
@ -185,19 +181,27 @@ export const GameStorage = {
|
||||
const y = dateObj.getFullYear();
|
||||
const m = dateObj.getMonth() + 1;
|
||||
const d = dateObj.getDate();
|
||||
const segmented = player.speedrun.isSegmented;
|
||||
Speedrun.setSegmented(true);
|
||||
const save = this.exportModifiedSave();
|
||||
download(
|
||||
`AD Save, Slot ${GameStorage.currentSlot + 1}${saveFileName} #${player.options.exportedFileCount} \
|
||||
(${y}-${m}-${d}).txt`, GameSaveSerializer.serialize(player));
|
||||
Speedrun.setSegmented(segmented);
|
||||
(${y}-${m}-${d}).txt`, save);
|
||||
GameUI.notify.info("Successfully downloaded current save file to your computer");
|
||||
},
|
||||
|
||||
// There are a couple props which may need to export with different values, so we handle that here
|
||||
exportModifiedSave() {
|
||||
// Speedrun segmented is exported as true
|
||||
const segmented = player.speedrun.isSegmented;
|
||||
Speedrun.setSegmented(true);
|
||||
|
||||
// Serialize the altered data, then restore the old prop values afterwards and return
|
||||
const save = GameSaveSerializer.serialize(player);
|
||||
Speedrun.setSegmented(segmented);
|
||||
return save;
|
||||
},
|
||||
|
||||
hardReset() {
|
||||
const IAP = JSON.parse(JSON.stringify(player.IAP));
|
||||
this.loadPlayerObject(Player.defaultStart);
|
||||
player.IAP = IAP;
|
||||
this.save(true);
|
||||
Tab.dimensions.antimatter.show();
|
||||
Cloud.resetTempState();
|
||||
@ -244,7 +248,7 @@ export const GameStorage = {
|
||||
V.updateTotalRunUnlocks();
|
||||
Enslaved.boostReality = false;
|
||||
GameEnd.additionalEnd = 0;
|
||||
Theme.set(player.options.theme);
|
||||
Theme.set(Theme.currentName());
|
||||
Notations.find(player.options.notation).setAsCurrent(true);
|
||||
ADNotations.Settings.exponentCommas.show = player.options.commas;
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
import { DC } from "./constants";
|
||||
|
||||
export function getTickSpeedMultiplier() {
|
||||
if (InfinityChallenge(3).isRunning) return DC.D1;
|
||||
if (Ra.isRunning) return DC.C1D1_1245;
|
||||
export function effectiveBaseGalaxies() {
|
||||
// Note that this already includes the "50% more" active path effect
|
||||
let replicantiGalaxies = Replicanti.galaxies.bought;
|
||||
replicantiGalaxies *= (1 + Effects.sum(
|
||||
@ -18,39 +16,14 @@ export function getTickSpeedMultiplier() {
|
||||
replicantiGalaxies += nonActivePathReplicantiGalaxies * Effects.sum(EternityChallenge(8).reward);
|
||||
let freeGalaxies = player.dilation.totalTachyonGalaxies;
|
||||
freeGalaxies *= 1 + Math.max(0, Replicanti.amount.log10() / 1e6) * AlchemyResource.alternation.effectValue;
|
||||
let galaxies = Math.max(player.galaxies + replicantiGalaxies + freeGalaxies + GalaxyGenerator.galaxies, 0);
|
||||
if (galaxies < 3) {
|
||||
// Magic numbers are to retain balancing from before while displaying
|
||||
// them now as positive multipliers rather than negative percentages
|
||||
let baseMultiplier = 1 / 1.1245;
|
||||
if (player.galaxies === 1) baseMultiplier = 1 / 1.11888888;
|
||||
if (player.galaxies === 2) baseMultiplier = 1 / 1.11267177;
|
||||
if (NormalChallenge(5).isRunning) {
|
||||
baseMultiplier = 1 / 1.08;
|
||||
if (player.galaxies === 1) baseMultiplier = 1 / 1.07632;
|
||||
if (player.galaxies === 2) baseMultiplier = 1 / 1.072;
|
||||
}
|
||||
const perGalaxy = 0.02 * Effects.product(
|
||||
InfinityUpgrade.galaxyBoost,
|
||||
InfinityUpgrade.galaxyBoost.chargedEffect,
|
||||
BreakInfinityUpgrade.galaxyBoost,
|
||||
TimeStudy(212),
|
||||
TimeStudy(232),
|
||||
Achievement(86),
|
||||
Achievement(178),
|
||||
InfinityChallenge(5).reward,
|
||||
PelleUpgrade.galaxyPower,
|
||||
PelleRifts.decay.milestones[1]
|
||||
);
|
||||
if (Pelle.isDoomed) galaxies *= 0.5;
|
||||
return Math.max(player.galaxies + GalaxyGenerator.galaxies + replicantiGalaxies + freeGalaxies, 0);
|
||||
}
|
||||
|
||||
galaxies *= Pelle.specialGlyphEffect.power;
|
||||
return DC.D0_01.clampMin(baseMultiplier - (galaxies * perGalaxy));
|
||||
}
|
||||
let baseMultiplier = 0.8;
|
||||
if (NormalChallenge(5).isRunning) baseMultiplier = 0.83;
|
||||
galaxies -= 2;
|
||||
galaxies *= Effects.product(
|
||||
export function getTickSpeedMultiplier() {
|
||||
if (InfinityChallenge(3).isRunning) return DC.D1;
|
||||
if (Ra.isRunning) return DC.C1D1_1245;
|
||||
let galaxies = effectiveBaseGalaxies();
|
||||
const effects = Effects.product(
|
||||
InfinityUpgrade.galaxyBoost,
|
||||
InfinityUpgrade.galaxyBoost.chargedEffect,
|
||||
BreakInfinityUpgrade.galaxyBoost,
|
||||
@ -62,6 +35,27 @@ export function getTickSpeedMultiplier() {
|
||||
PelleUpgrade.galaxyPower,
|
||||
PelleRifts.decay.milestones[1]
|
||||
);
|
||||
if (galaxies < 3) {
|
||||
// Magic numbers are to retain balancing from before while displaying
|
||||
// them now as positive multipliers rather than negative percentages
|
||||
let baseMultiplier = 1 / 1.1245;
|
||||
if (player.galaxies === 1) baseMultiplier = 1 / 1.11888888;
|
||||
if (player.galaxies === 2) baseMultiplier = 1 / 1.11267177;
|
||||
if (NormalChallenge(5).isRunning) {
|
||||
baseMultiplier = 1 / 1.08;
|
||||
if (player.galaxies === 1) baseMultiplier = 1 / 1.07632;
|
||||
if (player.galaxies === 2) baseMultiplier = 1 / 1.072;
|
||||
}
|
||||
const perGalaxy = 0.02 * effects;
|
||||
if (Pelle.isDoomed) galaxies *= 0.5;
|
||||
|
||||
galaxies *= Pelle.specialGlyphEffect.power;
|
||||
return DC.D0_01.clampMin(baseMultiplier - (galaxies * perGalaxy));
|
||||
}
|
||||
let baseMultiplier = 0.8;
|
||||
if (NormalChallenge(5).isRunning) baseMultiplier = 0.83;
|
||||
galaxies -= 2;
|
||||
galaxies *= effects;
|
||||
galaxies *= getAdjustedGlyphEffect("cursedgalaxies");
|
||||
galaxies *= getAdjustedGlyphEffect("realitygalaxies");
|
||||
galaxies *= 1 + ImaginaryUpgrade(9).effectOrDefault(0);
|
||||
@ -78,6 +72,7 @@ export function buyTickSpeed() {
|
||||
if (NormalChallenge(9).isRunning) {
|
||||
Tickspeed.multiplySameCosts();
|
||||
}
|
||||
Tutorial.turnOffEffect(TUTORIAL_STATE.TICKSPEED);
|
||||
Currency.antimatter.subtract(Tickspeed.cost);
|
||||
player.totalTickBought++;
|
||||
player.records.thisInfinity.lastBuyTime = player.records.thisInfinity.time;
|
||||
@ -91,6 +86,7 @@ export function buyMaxTickSpeed() {
|
||||
if (!Tickspeed.isAvailableForPurchase || !Tickspeed.isAffordable) return;
|
||||
let boughtTickspeed = false;
|
||||
|
||||
Tutorial.turnOffEffect(TUTORIAL_STATE.TICKSPEED);
|
||||
if (NormalChallenge(9).isRunning || InfinityChallenge(5).isRunning) {
|
||||
const goal = Player.infinityGoal;
|
||||
let cost = Tickspeed.cost;
|
||||
@ -171,16 +167,20 @@ export const Tickspeed = {
|
||||
},
|
||||
|
||||
get baseValue() {
|
||||
let boughtTickspeed;
|
||||
if (Laitela.continuumActive) boughtTickspeed = this.continuumValue;
|
||||
else boughtTickspeed = player.totalTickBought;
|
||||
return DC.E3.timesEffectsOf(
|
||||
Achievement(36),
|
||||
Achievement(45),
|
||||
Achievement(66),
|
||||
Achievement(83)
|
||||
)
|
||||
.times(getTickSpeedMultiplier().pow(boughtTickspeed + player.totalTickGained));
|
||||
.times(getTickSpeedMultiplier().pow(this.totalUpgrades));
|
||||
},
|
||||
|
||||
get totalUpgrades() {
|
||||
let boughtTickspeed;
|
||||
if (Laitela.continuumActive) boughtTickspeed = this.continuumValue;
|
||||
else boughtTickspeed = player.totalTickBought;
|
||||
return boughtTickspeed + player.totalTickGained;
|
||||
},
|
||||
|
||||
get perSecond() {
|
||||
|
@ -75,6 +75,16 @@ export const Time = {
|
||||
this.toMilliseconds(timespan, value => player.records.totalTimePlayed = value);
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {TimeSpan}
|
||||
*/
|
||||
get timeSinceBlackHole() {
|
||||
return this.fromMilliseconds(() => {
|
||||
const diff = player.records.totalTimePlayed - player.records.timePlayedAtBHUnlock;
|
||||
return Math.max(0, diff);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {TimeSpan}
|
||||
*/
|
||||
|
@ -144,8 +144,6 @@ class TabState {
|
||||
const tabNotificationKey = this.key + this._currentSubtab.key;
|
||||
if (player.tabNotifications.has(tabNotificationKey)) player.tabNotifications.delete(tabNotificationKey);
|
||||
|
||||
// Makes it so that the glyph tooltip doesn't stay on tab change
|
||||
ui.view.tabs.reality.currentGlyphTooltip = -1;
|
||||
if (manual) Modal.hideAll();
|
||||
EventHub.dispatch(GAME_EVENT.TAB_CHANGED, this, this._currentSubtab);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import { deepmergeAll } from "@/utility/deepmerge";
|
||||
import { playFabLogin } from "./core/playfab";
|
||||
import { SpeedrunMilestones } from "./core/speedrun";
|
||||
import { supportedBrowsers } from "./supported-browsers";
|
||||
|
||||
import Payments from "./core/payments";
|
||||
|
||||
if (GlobalErrorHandler.handled) {
|
||||
@ -321,9 +322,9 @@ export function getGameSpeedupFactor(effectsToConsider, blackHolesActiveOverride
|
||||
|
||||
let factor = 1;
|
||||
if (effects.includes(GAME_SPEED_EFFECT.BLACK_HOLE)) {
|
||||
if (BlackHoles.arePaused) {
|
||||
if (BlackHoles.areNegative) {
|
||||
factor *= player.blackHoleNegative;
|
||||
} else {
|
||||
} else if (!BlackHoles.arePaused) {
|
||||
for (const blackHole of BlackHoles.list) {
|
||||
if (!blackHole.isUnlocked) break;
|
||||
const isActive = blackHolesActiveOverride === undefined
|
||||
@ -634,7 +635,8 @@ export function gameLoop(passDiff, options = {}) {
|
||||
function passivePrestigeGen() {
|
||||
let eternitiedGain = 0;
|
||||
if (RealityUpgrade(14).isBought) {
|
||||
eternitiedGain = Effects.product(
|
||||
eternitiedGain = DC.D1.timesEffectsOf(
|
||||
Achievement(113),
|
||||
RealityUpgrade(3),
|
||||
RealityUpgrade(14)
|
||||
);
|
||||
@ -722,11 +724,31 @@ function laitelaRealityTick(realDiff) {
|
||||
}
|
||||
if (Laitela.realityReward > oldInfo.realityReward) {
|
||||
completionText += `<br><br>Dark Matter Multiplier: ${formatX(oldInfo.realityReward, 2, 2)}
|
||||
➜ ${formatX(Laitela.realityReward, 2, 2)}
|
||||
<br>Best Completion Time: ${TimeSpan.fromSeconds(oldInfo.fastestCompletion).toStringShort()}
|
||||
(${formatInt(8 - oldInfo.difficultyTier)}) ➜
|
||||
${TimeSpan.fromSeconds(laitelaInfo.fastestCompletion).toStringShort()}
|
||||
(${formatInt(8 - laitelaInfo.difficultyTier)})`;
|
||||
➜ ${formatX(Laitela.realityReward, 2, 2)}`;
|
||||
if (oldInfo.fastestCompletion === 3600 || oldInfo.fastestCompletion === 300 && oldInfo.difficultyTier > 0) {
|
||||
if (Time.thisRealityRealTime.totalSeconds < 30) {
|
||||
// First attempt - destabilising
|
||||
completionText += `<br>Best Completion Time: None ➜ Destabilized
|
||||
<br>Highest Active Dimension: ${formatInt(8 - oldInfo.difficultyTier)} ➜
|
||||
${formatInt(8 - laitelaInfo.difficultyTier)}`;
|
||||
} else {
|
||||
// First attempt - not destabilising
|
||||
completionText += `<br>Best Completion Time: None ➜
|
||||
${TimeSpan.fromSeconds(laitelaInfo.fastestCompletion).toStringShort()}
|
||||
<br>Highest Active Dimension: ${formatInt(8 - laitelaInfo.difficultyTier)}`;
|
||||
}
|
||||
} else if (Time.thisRealityRealTime.totalSeconds < 30) {
|
||||
// Second+ attempt - destabilising
|
||||
completionText += `<br>Best Completion Time: ${TimeSpan.fromSeconds(oldInfo.fastestCompletion).toStringShort()}
|
||||
➜ Destablized
|
||||
<br>Highest Active Dimension: ${formatInt(8 - oldInfo.difficultyTier)} ➜
|
||||
${formatInt(8 - laitelaInfo.difficultyTier)}`;
|
||||
} else {
|
||||
// Second+ attempt - not destabilising
|
||||
completionText += `<br>Best Completion Time: ${TimeSpan.fromSeconds(oldInfo.fastestCompletion).toStringShort()}
|
||||
➜ ${TimeSpan.fromSeconds(laitelaInfo.fastestCompletion).toStringShort()}
|
||||
<br>Highest Active Dimension: ${formatInt(8 - oldInfo.difficultyTier)}`;
|
||||
}
|
||||
player.records.bestReality.laitelaSet = Glyphs.copyForRecords(Glyphs.active.filter(g => g !== null));
|
||||
} else {
|
||||
completionText += ` You need to destabilize in faster than
|
||||
@ -1040,6 +1062,8 @@ export function init() {
|
||||
Tabs.all.find(t => t.config.id === player.options.lastOpenTab).show(true);
|
||||
kong.init();
|
||||
if(steamOn){SteamFunctions.UIZoom()}
|
||||
shop.init();
|
||||
Payments.init();
|
||||
}
|
||||
|
||||
window.tweenTime = 0;
|
||||
|
@ -1002,23 +1002,6 @@
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.c-glyph-choice-container {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
width: 74rem;
|
||||
}
|
||||
|
||||
.c-glyph-choice-single-glyph {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 36rem;
|
||||
height: 12rem;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
border-radius: var(--var-border-radius, 0.5rem);
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.c-glyph-choice-icon {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -277,6 +277,7 @@ body.t-s9 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.l-antimatter-dim-tab {
|
||||
@ -635,7 +636,7 @@ body.t-s9 {
|
||||
}
|
||||
|
||||
.t-inverted .c-autobuyer-box-row__modal,
|
||||
.t-inverted-metro .c-autobuyer-box-row__modal {
|
||||
.t-inverted-metro .c-autobuyer-box-row__modal {
|
||||
background-color: var(--color-base);
|
||||
border-color: var(--color-text);
|
||||
}
|
||||
|
@ -61,6 +61,7 @@
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.s-base--metro .o-tab-btn {
|
||||
|
@ -61,7 +61,7 @@ html {
|
||||
--color-bad: #b84b5f;
|
||||
--color-gh-purple: #8957e5;
|
||||
--color-notification: red;
|
||||
--color-overlay: rgba(30, 30, 50, 90%);
|
||||
--color-overlay: rgba(30, 30, 50, 70%);
|
||||
|
||||
--color-antimatter: #2196f3;
|
||||
--color-infinity: #b67f33;
|
||||
@ -330,7 +330,6 @@ button:focus {
|
||||
}
|
||||
|
||||
.c-tooltip-content {
|
||||
content: attr(ach-tooltip);
|
||||
width: 16rem;
|
||||
position: absolute;
|
||||
z-index: 4;
|
||||
@ -1464,6 +1463,7 @@ br {
|
||||
border-radius: var(--var-border-radius, 0.5rem);
|
||||
padding: 0.5rem;
|
||||
transition: opacity 0.3s, visibility 0.3s;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.s-base--dark .infotooltip .infotooltiptext {
|
||||
@ -1601,16 +1601,16 @@ br {
|
||||
}
|
||||
|
||||
.o-primary-btn--dimboost {
|
||||
position: relative;
|
||||
width: 21rem;
|
||||
height: 4.5rem;
|
||||
position: relative;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.o-primary-btn--galaxy {
|
||||
position: relative;
|
||||
width: 21rem;
|
||||
height: 4.5rem;
|
||||
position: relative;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
@ -2304,6 +2304,9 @@ br {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
color: var(--color-text);
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.l-game-header__buttons-line {
|
||||
@ -2501,6 +2504,7 @@ br {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.l-infinity-dim-tab__ec8-purchases {
|
||||
@ -2615,6 +2619,7 @@ br {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
/* #endregion l-time-dim-tab */
|
||||
@ -4187,11 +4192,6 @@ br {
|
||||
border-color: var(--color-text);
|
||||
}
|
||||
|
||||
.t-s6 .o-autobuyer-toggle-checkbox__label,
|
||||
.t-s10 .o-autobuyer-toggle-checkbox__label {
|
||||
border-color: #cccccc;
|
||||
}
|
||||
|
||||
.t-s6 .o-autobuyer-toggle-checkbox__label,
|
||||
.t-s10 .o-autobuyer-toggle-checkbox__label {
|
||||
border-color: var(--color-text);
|
||||
@ -4391,7 +4391,7 @@ br {
|
||||
|
||||
.c-autobuyer-box-row {
|
||||
display: flex;
|
||||
width: 90rem;
|
||||
width: 96rem;
|
||||
position: relative;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@ -4467,7 +4467,7 @@ properly on certain themes. */
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-wrap: wrap;
|
||||
width: 91rem;
|
||||
width: 97rem;
|
||||
}
|
||||
|
||||
.c-small-autobuyer-input {
|
||||
@ -4746,6 +4746,7 @@ properly on certain themes. */
|
||||
.o-eternity-upgrade--useless {
|
||||
color: black;
|
||||
background-color: var(--color-pelle--base);
|
||||
filter: grayscale(50%);
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
@ -5208,6 +5209,7 @@ properly on certain themes. */
|
||||
.o-dilation-upgrade--useless {
|
||||
color: black;
|
||||
background-color: var(--color-pelle--base);
|
||||
filter: grayscale(50%);
|
||||
}
|
||||
|
||||
.o-dilation-upgrade--unavailable {
|
||||
@ -5245,10 +5247,6 @@ properly on certain themes. */
|
||||
background-color: #d72621;
|
||||
}
|
||||
|
||||
.t-dark .o-dilation-upgrade {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
.t-dark .o-dilation-upgrade--available:hover,
|
||||
.t-s6 .o-dilation-upgrade--available:hover,
|
||||
.t-s10 .o-dilation-upgrade--available:hover {
|
||||
@ -5783,7 +5781,7 @@ properly on certain themes. */
|
||||
.c-modal-IAP__warning {
|
||||
font-size: 1.6rem;
|
||||
font-weight: bold;
|
||||
color: #ff6666;
|
||||
color: var(--color-notification);
|
||||
}
|
||||
|
||||
/* #endregion c-modal-import */
|
||||
@ -7383,8 +7381,8 @@ kbd {
|
||||
.c-ra-run-button {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 31rem;
|
||||
height: 33rem;
|
||||
width: 33rem;
|
||||
height: 36rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: Typewriter;
|
||||
@ -7461,8 +7459,8 @@ kbd {
|
||||
.c-ra-remembrance-unlock {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 31rem;
|
||||
height: 33rem;
|
||||
width: 33rem;
|
||||
height: 36rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
@ -7636,8 +7634,8 @@ kbd {
|
||||
left: 50%;
|
||||
z-index: 0;
|
||||
border-top: 0 solid black;
|
||||
border-right: var(--var-border-width, 0.7rem) solid transparent;
|
||||
border-left: var(--var-border-width, 0.7rem) solid transparent;
|
||||
border-right: 0.7rem solid transparent;
|
||||
border-left: 0.7rem solid transparent;
|
||||
margin-bottom: 0;
|
||||
margin-left: -0.7rem;
|
||||
transition-duration: 0.3s;
|
||||
@ -8087,7 +8085,7 @@ kbd {
|
||||
}
|
||||
|
||||
.o-laitela-run-button--large {
|
||||
width: 45rem;
|
||||
width: 44rem;
|
||||
}
|
||||
|
||||
.o-laitela-matter-amount {
|
||||
@ -8098,7 +8096,7 @@ kbd {
|
||||
.c-dark-matter-dimension-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 50rem;
|
||||
width: 51rem;
|
||||
height: 15rem;
|
||||
color: var(--color-laitela--accent);
|
||||
background: var(--color-laitela--base);
|
||||
@ -8124,6 +8122,7 @@ kbd {
|
||||
width: 15rem;
|
||||
height: 5rem;
|
||||
font-family: Typewriter;
|
||||
font-size: 1.2rem;
|
||||
color: var(--color-laitela--accent);
|
||||
background: var(--color-laitela--base);
|
||||
border: var(--var-border-width, 0.2rem) solid var(--color-laitela--accent);
|
||||
@ -8210,7 +8209,7 @@ kbd {
|
||||
.c-laitela-milestone {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 22rem;
|
||||
width: 21rem;
|
||||
height: 16%;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
@ -8261,9 +8260,9 @@ kbd {
|
||||
|
||||
.c-laitela-milestone--completed {
|
||||
width: 18.8rem;
|
||||
height: 21.8rem;
|
||||
height: 20.8rem;
|
||||
top: -1.5rem;
|
||||
left: 1.5rem;
|
||||
left: 1rem;
|
||||
background-color: transparent;
|
||||
background-image: url("../images/laitela-icon.svg");
|
||||
background-position: center;
|
||||
@ -8281,8 +8280,6 @@ kbd {
|
||||
background: var(--color-laitela--base);
|
||||
border: var(--var-border-width, 0.2rem) solid var(--color-laitela--accent);
|
||||
border-radius: var(--var-border-radius, 0.5rem);
|
||||
margin: auto;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
@ -8362,7 +8359,7 @@ kbd {
|
||||
.l-singularity-milestone-modal-container-inner {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 93rem;
|
||||
width: 111rem;
|
||||
height: 120rem;
|
||||
justify-content: space-evenly;
|
||||
padding-bottom: 2rem;
|
||||
@ -8379,12 +8376,12 @@ kbd {
|
||||
left: -0.5rem;
|
||||
border: 0.1rem solid var(--color-laitela--accent);
|
||||
border-radius: var(--var-border-radius, 0.6rem);
|
||||
margin-top: 0.5rem;
|
||||
margin: 1rem 0 0 1rem;
|
||||
padding: 1rem 0.5rem;
|
||||
}
|
||||
|
||||
.c-singularity-milestone-modal-sort-button {
|
||||
width: 22rem;
|
||||
width: 26.5rem;
|
||||
height: 6rem;
|
||||
font-family: Typewriter, serif;
|
||||
font-size: 1.3rem;
|
||||
@ -8404,7 +8401,7 @@ kbd {
|
||||
}
|
||||
|
||||
.o-laitela-singularity-modal-button {
|
||||
width: 22rem;
|
||||
width: 21rem;
|
||||
height: 4%;
|
||||
font-weight: bold;
|
||||
color: var(--color-laitela--accent);
|
||||
@ -8480,7 +8477,7 @@ kbd {
|
||||
}
|
||||
|
||||
.l-laitela-annihilation-container {
|
||||
width: 50rem;
|
||||
width: 51rem;
|
||||
height: 20rem;
|
||||
font-family: Typewriter, serif;
|
||||
font-size: 1.2rem;
|
||||
@ -8525,7 +8522,7 @@ kbd {
|
||||
}
|
||||
|
||||
.c-laitela-automation-toggle {
|
||||
width: 20rem;
|
||||
width: 22rem;
|
||||
height: 3rem;
|
||||
font-family: Typewriter;
|
||||
font-size: 1.1rem;
|
||||
@ -9551,7 +9548,7 @@ input.o-automator-block-input {
|
||||
|
||||
.c-autobuyer-buy-box {
|
||||
display: flex;
|
||||
width: 90rem;
|
||||
width: 96rem;
|
||||
height: 6.4rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
@ -47,6 +47,7 @@
|
||||
border: 0.1rem solid;
|
||||
border-radius: var(--var-border-radius, 0.4rem);
|
||||
transition-duration: 0.2s;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.c-pelle-useless-available {
|
||||
|
@ -16,7 +16,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
update() {
|
||||
this.blob = player.options.theme === "S11";
|
||||
this.blob = Theme.currentName() === "S11";
|
||||
this.animateTachyons = player.options.animations.tachyonParticles &&
|
||||
Tabs.current[this.$viewModel.subtab].name === "Time Dilation";
|
||||
}
|
||||
|
@ -48,6 +48,8 @@ export default {
|
||||
const challengeNotEnterable = !this.isUnlocked || this.isRunning || this.name === "C1";
|
||||
return {
|
||||
"o-challenge-btn": true,
|
||||
"o-challenge-btn--broken": this.overrideLabel.length > 0 && this.name !== "C10",
|
||||
"o-challenge-btn--broken-alt": this.overrideLabel.length > 0 && this.name === "C10",
|
||||
"o-challenge-btn--running": this.isRunning || this.inC1,
|
||||
"o-challenge-btn--completed": this.isCompleted && this.isUnlocked,
|
||||
"o-challenge-btn--unlocked": !this.isCompleted && this.isUnlocked,
|
||||
@ -96,5 +98,15 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.o-challenge-btn--broken {
|
||||
background: var(--color-enslaved--base);
|
||||
clip-path: polygon(0% 0%, 25% 20%, 95% 0%, 100% 25%, 80% 70%, 95% 50%, 100% 100%, 45% 95%,
|
||||
65% 70%, 15% 95%, 0% 45%, 10% 50%);
|
||||
}
|
||||
|
||||
.o-challenge-btn--broken-alt {
|
||||
background: var(--color-enslaved--base);
|
||||
clip-path: polygon(0% 0%, 15% 0%, 25% 40%, 30% 0%, 55% 0%, 85% 30%, 75% 0%, 100% 0%,
|
||||
90% 40%, 100% 65%, 90% 95%, 45% 45%, 70% 100%, 25% 100%, 5% 90%, 10% 60%);
|
||||
}
|
||||
</style>
|
||||
|
@ -1,4 +1,6 @@
|
||||
<script>
|
||||
import wordShift from "../../javascripts/core/wordShift";
|
||||
|
||||
import { isFunction, isString } from "@/utility";
|
||||
|
||||
/* eslint-disable no-empty-function */
|
||||
@ -71,9 +73,19 @@ export default {
|
||||
const value = description();
|
||||
|
||||
if (isString(value)) {
|
||||
// This is a special case for scrambling EC6 description text
|
||||
if (this.config.scrambleText) {
|
||||
this.description = capitalize(value).replace("*", wordShift.wordCycle(this.config.scrambleText, true));
|
||||
this.updateFunction = () =>
|
||||
this.description = capitalize(description())
|
||||
.replace("*", wordShift.wordCycle(this.config.scrambleText, true));
|
||||
return;
|
||||
}
|
||||
this.description = capitalize(value);
|
||||
this.updateFunction = () => this.description = capitalize(description());
|
||||
return;
|
||||
|
||||
|
||||
}
|
||||
|
||||
throw new Error(`DescriptionDisplay config.description is a function ` +
|
||||
|
@ -38,10 +38,9 @@ export default {
|
||||
update() {
|
||||
this.baseSpeed = getGameSpeedupFactor();
|
||||
this.pulsedSpeed = getGameSpeedupForDisplay();
|
||||
const ec12 = EternityChallenge(12);
|
||||
this.hasSeenAlteredSpeed = PlayerProgress.realityUnlocked() || ec12.completions > 0 || ec12.isRunning;
|
||||
this.hasSeenAlteredSpeed = PlayerProgress.seenAlteredSpeed();
|
||||
this.isStopped = Enslaved.isStoringRealTime;
|
||||
this.isEC12 = ec12.isRunning;
|
||||
this.isEC12 = EternityChallenge(12).isRunning;
|
||||
this.isPulsing = (this.baseSpeed !== this.pulsedSpeed) && Enslaved.canRelease(true);
|
||||
},
|
||||
formatNumber(num) {
|
||||
@ -58,14 +57,17 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<b>
|
||||
<span class="c-gamespeed">
|
||||
<span>
|
||||
{{ baseText }}
|
||||
</span>
|
||||
<span v-if="isPulsing">(<i class="fas fa-expand-arrows-alt u-fa-padding" /> {{ pulseSpeedText }})</span>
|
||||
</b>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.c-gamespeed {
|
||||
font-weight: bold;
|
||||
color: var(--color-text);
|
||||
}
|
||||
</style>
|
||||
|
@ -96,6 +96,7 @@ export default {
|
||||
// This flag is used to prevent the tooltip from being shown in some touch event sequences
|
||||
suppressTooltip: false,
|
||||
isTouched: false,
|
||||
tooltipEnabled: false,
|
||||
sacrificeReward: 0,
|
||||
uncappedRefineReward: 0,
|
||||
refineReward: 0,
|
||||
@ -307,6 +308,14 @@ export default {
|
||||
this.$recompute("displayedInfo");
|
||||
});
|
||||
this.on$("tooltip-touched", () => this.hideTooltip());
|
||||
this.on$(GAME_EVENT.TAB_CHANGED, () => this.hideTooltip());
|
||||
|
||||
// There are a few situations where a tooltip could attempt to render immediately upon component creation,
|
||||
// which causes it to be placed in an odd "default" corner spot due to mouse position not being set properly.
|
||||
// This is essentially a hack that force-suppresses tooltips from being shown in strange spots due to on-load
|
||||
// events firing, but has the side effect that the mouse must leave and enter an element which was created
|
||||
// underneath it in order to make the tooltip appear
|
||||
setTimeout(() => this.tooltipEnabled = true, 10);
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.isCurrentTooltip) this.hideTooltip();
|
||||
@ -361,6 +370,7 @@ export default {
|
||||
this.$viewModel.tabs.reality.currentGlyphTooltip = -1;
|
||||
},
|
||||
showTooltip() {
|
||||
if (!this.tooltipEnabled) return;
|
||||
Glyphs.removeNewFlag(this.glyph);
|
||||
this.tooltipLoaded = true;
|
||||
this.$viewModel.tabs.reality.mouseoverGlyphInfo.inInventory = !this.circular;
|
||||
|
@ -98,7 +98,7 @@ export default {
|
||||
Modal.glyphShowcasePanel.show({
|
||||
name: this.text,
|
||||
glyphSet: this.glyphs,
|
||||
closeOn: GAME_EVENT.GLYPH_SET_SAVE_CHANGE,
|
||||
closeEvent: GAME_EVENT.GLYPH_SET_SAVE_CHANGE,
|
||||
displaySacrifice: this.showSacrifice,
|
||||
});
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ export default {
|
||||
"o-infinity-upgrade-btn--available": !this.isUseless && !this.isBought && this.canBeBought,
|
||||
"o-infinity-upgrade-btn--unavailable": !this.isUseless && !this.isBought && !this.canBeBought,
|
||||
"o-infinity-upgrade-btn--useless": this.isUseless,
|
||||
"o-pelle-disabled": this.isUseless,
|
||||
"o-infinity-upgrade-btn--chargeable": !this.isCharged && this.chargePossible &&
|
||||
(this.showingCharged || this.shiftDown),
|
||||
"o-infinity-upgrade-btn--charged": this.isCharged,
|
||||
|
@ -11,15 +11,12 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
localString() {
|
||||
const desc = this.cloudSaveEnabled
|
||||
? "Time since last local save:"
|
||||
: "Time since last save:";
|
||||
return `${desc} ${timeDisplayShort(this.currentTime - this.lastLocalSave)}`;
|
||||
},
|
||||
cloudString() {
|
||||
if (!this.cloudSaveEnabled) return null;
|
||||
return `Time since last cloud save: ${timeDisplayShort(this.currentTime - this.lastCloudSave)}`;
|
||||
timeString() {
|
||||
const localStr = timeDisplayShort(this.currentTime - this.lastLocalSave);
|
||||
const cloudStr = timeDisplayShort(this.currentTime - this.lastCloudSave);
|
||||
return this.cloudSaveEnabled
|
||||
? `${localStr} (local) | ${cloudStr} (cloud)`
|
||||
: localStr;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@ -43,9 +40,7 @@ export default {
|
||||
class="o-save-timer"
|
||||
@click="save"
|
||||
>
|
||||
{{ localString }}
|
||||
<br>
|
||||
{{ cloudString }}
|
||||
Time since last save: {{ timeString }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -5,6 +5,7 @@ export default {
|
||||
return {
|
||||
isActive: false,
|
||||
isSegmented: false,
|
||||
usedSTD: false,
|
||||
hasStarted: false,
|
||||
startDate: 0,
|
||||
saveName: "",
|
||||
@ -25,6 +26,9 @@ export default {
|
||||
segmentText() {
|
||||
return this.isSegmented ? "Segmented Speedrun (imported save)" : "Single-segment Speedrun (no save import)";
|
||||
},
|
||||
iapText() {
|
||||
return this.usedSTD ? "IAPs have been used" : "No IAPs Used";
|
||||
},
|
||||
offlineText() {
|
||||
const stateText = this.offlineProgress
|
||||
? `<span style="color: var(--color-good)">Enabled</span>`
|
||||
@ -48,6 +52,7 @@ export default {
|
||||
// Short-circuit if speedrun isn't active; updating some later stuff can cause vue errors outside of speedruns
|
||||
if (!this.isActive) return;
|
||||
this.isSegmented = speedrun.isSegmented;
|
||||
this.usedSTD = speedrun.usedSTD;
|
||||
this.hasStarted = speedrun.hasStarted;
|
||||
this.startDate = speedrun.startDate;
|
||||
this.saveName = speedrun.name;
|
||||
@ -98,6 +103,8 @@ export default {
|
||||
<br>
|
||||
<i>{{ segmentText }}</i>
|
||||
<br>
|
||||
<i>{{ iapText }}</i>
|
||||
<br>
|
||||
Total real playtime since start: {{ timePlayedStr }}
|
||||
<br>
|
||||
Offline Progress: <span v-html="offlineText" />
|
||||
|
@ -72,13 +72,10 @@ export default {
|
||||
},
|
||||
typeStyle() {
|
||||
// Special case for cursed glyphs because its black default has poor contrast on some themes
|
||||
// TODO Update this when we "fix" Modern UI Normal because #5151EC looks kinda weird
|
||||
const color = this.glyph.type === "cursed"
|
||||
? "#5151EC"
|
||||
: GlyphTypes[this.type].color;
|
||||
return {
|
||||
color: `${color}`,
|
||||
color: GlyphTypes[this.type].color,
|
||||
"font-weight": "bold",
|
||||
"text-shadow": this.type === "cursed" ? "0.05rem 0.05rem var(--color-text)" : undefined,
|
||||
animation: this.type === "reality" ? "a-reality-glyph-description-cycle 10s infinite" : undefined,
|
||||
};
|
||||
},
|
||||
|
@ -19,10 +19,6 @@ export default {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
closeOn: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
isGlyphSelection: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@ -52,9 +48,12 @@ export default {
|
||||
}
|
||||
return maxEffects;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.on$(this.closeOn, this.emitClose);
|
||||
containerClass() {
|
||||
return {
|
||||
"c-glyph-choice-container": true,
|
||||
"c-glyph-choice-container-single": this.glyphs.length === 1,
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
update() {
|
||||
@ -85,7 +84,7 @@ export default {
|
||||
:glyph-set="glyphs"
|
||||
:force-color="true"
|
||||
/>
|
||||
<div class="c-glyph-choice-container">
|
||||
<div :class="containerClass">
|
||||
<GlyphShowcasePanelEntry
|
||||
v-for="(glyph, idx) in glyphs"
|
||||
:key="idx"
|
||||
@ -100,3 +99,26 @@ export default {
|
||||
</div>
|
||||
</ModalWrapper>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.c-glyph-choice-container {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
width: 74rem;
|
||||
}
|
||||
|
||||
.c-glyph-choice-container-single {
|
||||
width: 37rem;
|
||||
}
|
||||
|
||||
.c-glyph-choice-single-glyph {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 36rem;
|
||||
height: 12rem;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
border-radius: var(--var-border-radius, 0.5rem);
|
||||
margin: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,87 +0,0 @@
|
||||
<script>
|
||||
import ModalWrapperChoice from "@/components/modals/ModalWrapperChoice";
|
||||
import PrimaryButton from "@/components/PrimaryButton";
|
||||
import SaveInfoEntry from "@/components/modals/cloud/SaveInfoEntry";
|
||||
|
||||
export default {
|
||||
name: "ImportFileWarningModal",
|
||||
components: {
|
||||
ModalWrapperChoice,
|
||||
PrimaryButton,
|
||||
SaveInfoEntry
|
||||
},
|
||||
props: {
|
||||
rawInput: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
saveToImport: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
warningMessage: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
importCounter: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
conflict() {
|
||||
return this.$viewModel.modal.cloudConflict;
|
||||
},
|
||||
clicksLeft() {
|
||||
return 5 - this.importCounter;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick() {
|
||||
this.importCounter++;
|
||||
if (this.clicksLeft > 0) return;
|
||||
this.emitClose();
|
||||
GameStorage.import(this.rawInput);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ModalWrapperChoice :show-confirm="false">
|
||||
<template #header>
|
||||
Possible Import Conflict
|
||||
</template>
|
||||
<div class="c-modal-message__text">
|
||||
You may not want to proceed with importing, as you may lose STD purchases.
|
||||
</div>
|
||||
<br>
|
||||
<SaveInfoEntry
|
||||
:save-data="conflict.importingSave"
|
||||
:other-data="conflict.currentSave"
|
||||
save-type="Save to Import"
|
||||
/>
|
||||
<br>
|
||||
<SaveInfoEntry
|
||||
:save-data="conflict.currentSave"
|
||||
:other-data="conflict.importingSave"
|
||||
save-type="Current Save"
|
||||
/>
|
||||
<span class="c-modal-IAP__warning">
|
||||
{{ warningMessage }}
|
||||
</span>
|
||||
Your purchased STDs will not carry over to the imported save.
|
||||
<br>
|
||||
Click Import five times if you still wish to import.
|
||||
<template #cancel-text>
|
||||
Keep Current
|
||||
</template>
|
||||
<PrimaryButton
|
||||
class="o-primary-btn--width-medium c-modal-message__okay-btn c-modal__confirm-btn"
|
||||
@click="handleClick"
|
||||
>
|
||||
Import <span v-if="clicksLeft">({{ clicksLeft }})</span>
|
||||
</PrimaryButton>
|
||||
</ModalWrapperChoice>
|
||||
</template>
|
@ -17,7 +17,6 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
input: "",
|
||||
importCounter: 0,
|
||||
offlineImport: OFFLINE_PROGRESS_TYPE.IMPORTED,
|
||||
};
|
||||
},
|
||||
@ -58,12 +57,6 @@ export default {
|
||||
inputIsSecret() {
|
||||
return isSecretImport(this.input) || Theme.isSecretTheme(this.input);
|
||||
},
|
||||
hasLessSTDs() {
|
||||
return player.IAP.totalSTD > (this.player?.IAP?.totalSTD ?? 0) && !this.inputIsSecret;
|
||||
},
|
||||
clicksLeft() {
|
||||
return 5 - this.importCounter;
|
||||
},
|
||||
timeSinceSave() {
|
||||
return TimeSpan.fromMilliseconds(Date.now() - this.player.lastUpdate).toString();
|
||||
},
|
||||
@ -124,8 +117,6 @@ export default {
|
||||
}
|
||||
},
|
||||
importSave() {
|
||||
this.importCounter++;
|
||||
if (this.hasLessSTDs && this.clicksLeft > 0) return;
|
||||
if (!this.inputIsValid) return;
|
||||
this.emitClose();
|
||||
GameStorage.import(this.input);
|
||||
@ -182,13 +173,6 @@ export default {
|
||||
</div>
|
||||
<span v-html="offlineDetails" />
|
||||
</div>
|
||||
<div
|
||||
v-if="hasLessSTDs"
|
||||
class="c-modal-IAP__warning"
|
||||
>
|
||||
IMPORTED SAVE HAS LESS STDs BOUGHT, YOU WILL LOSE THEM WITH YOUR SAVE.
|
||||
<br>CLICK THE BUTTON 5 TIMES TO CONFIRM.
|
||||
</div>
|
||||
</template>
|
||||
<div v-else-if="hasInput">
|
||||
Not a valid save:
|
||||
@ -202,7 +186,7 @@ export default {
|
||||
class="o-primary-btn--width-medium c-modal-message__okay-btn c-modal__confirm-btn"
|
||||
@click="importSave"
|
||||
>
|
||||
Import <span v-if="hasLessSTDs">({{ clicksLeft }})</span>
|
||||
Import
|
||||
</PrimaryButton>
|
||||
</ModalWrapperChoice>
|
||||
</template>
|
@ -29,7 +29,7 @@ export default {
|
||||
},
|
||||
createRealityGlyph() {
|
||||
if (GameCache.glyphInventorySpace.value === 0) {
|
||||
Modal.message.show("Inventory cannot hold new Glyphs. Purge some Glyphs.",
|
||||
Modal.message.show("No available inventory space; Sacrifice some Glyphs to free up space.",
|
||||
{ closeEvent: GAME_EVENT.GLYPHS_CHANGED });
|
||||
return;
|
||||
}
|
||||
|
@ -20,13 +20,18 @@ export default {
|
||||
return {
|
||||
target: 0,
|
||||
idx: 0,
|
||||
isDoomed: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
resetTerm() { return this.isDoomed ? "Armageddon" : "Reality"; },
|
||||
},
|
||||
methods: {
|
||||
update() {
|
||||
this.target = this.targetSlot;
|
||||
this.idx = this.inventoryIndex;
|
||||
this.glyph = Glyphs.findByInventoryIndex(this.idx);
|
||||
this.isDoomed = Pelle.isDoomed;
|
||||
},
|
||||
handleYesClick() {
|
||||
Glyphs.swapIntoActive(this.glyph, this.targetSlot);
|
||||
@ -43,6 +48,6 @@ export default {
|
||||
<template #header>
|
||||
You are about to replace a Glyph
|
||||
</template>
|
||||
Replacing a Glyph will restart this Reality.
|
||||
Replacing a Glyph will restart this {{ resetTerm }}.
|
||||
</ModalWrapperChoice>
|
||||
</template>
|
||||
|
@ -7,8 +7,16 @@ export default {
|
||||
ModalWrapperChoice
|
||||
},
|
||||
methods: {
|
||||
returnedSTDCount() {
|
||||
let std = 0;
|
||||
for (const purchase of ShopPurchase.all) {
|
||||
if (purchase.config.singleUse) continue;
|
||||
std += purchase.purchases * purchase.cost;
|
||||
}
|
||||
return std;
|
||||
},
|
||||
handleYesClick() {
|
||||
ShopPurchase.respecAll();
|
||||
ShopPurchaseData.respecAll();
|
||||
EventHub.ui.offAll(this);
|
||||
}
|
||||
},
|
||||
@ -24,12 +32,15 @@ export default {
|
||||
You are about to respec your Shop Purchases
|
||||
</template>
|
||||
<div class="c-modal-message__text">
|
||||
Are you sure you want to respec your Shop Purchases? This will not cost any
|
||||
Are you sure you want to respec your Shop Purchases? This will not cost anything and
|
||||
return the {{ returnedSTDCount() }}
|
||||
<img
|
||||
src="images/std_coin.png"
|
||||
class="o-shop-button-button__img"
|
||||
>.
|
||||
<!-- TODO: Should it cost any coins? -->
|
||||
> you spent on all non-offline progress purchases.
|
||||
<br>
|
||||
<br>
|
||||
<b class="o-warning">You will not be able to respec again unless you purchase more STD coins.</b>
|
||||
</div>
|
||||
</ModalWrapperChoice>
|
||||
</template>
|
||||
@ -43,4 +54,8 @@ export default {
|
||||
height: 2.5rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.o-warning {
|
||||
color: var(--color-infinity);
|
||||
}
|
||||
</style>
|
||||
|
@ -90,6 +90,7 @@ export default {
|
||||
<br>
|
||||
<br>
|
||||
<div class="c-modal-hard-reset-danger">
|
||||
<!-- TODO: Change the wording of this; originally this assumed it's only unlockable from a completed save -->
|
||||
Starting a speedrun will overwrite your current save, replacing this save with the new speedrun save. Export
|
||||
this save first if you want to keep a save with a beaten game. Type in "Gotta Go Fast!" below to confirm and
|
||||
start the run.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user