mirror of
https://github.com/IvarK/AntimatterDimensionsSourceCode.git
synced 2025-02-18 00:20:13 +00:00
914 lines
33 KiB
JavaScript
914 lines
33 KiB
JavaScript
"use strict";
|
|
|
|
if (GlobalErrorHandler.handled) {
|
|
throw new Error("Initialization failed");
|
|
}
|
|
GlobalErrorHandler.cleanStart = true;
|
|
|
|
function playerInfinityUpgradesOnReset() {
|
|
if (RealityUpgrade(10).isBought || EternityMilestone.keepBreakUpgrades.isReached) {
|
|
player.infinityUpgrades = new Set(
|
|
["timeMult", "dimMult", "timeMult2",
|
|
"skipReset1", "skipReset2", "unspentBonus",
|
|
"27Mult", "18Mult", "36Mult", "resetMult",
|
|
"skipReset3", "passiveGen", "45Mult",
|
|
"resetBoost", "galaxyBoost", "skipResetGalaxy",
|
|
"totalMult", "currentMult", "postGalaxy",
|
|
"challengeMult", "achievementMult", "infinitiedMult",
|
|
"infinitiedGeneration", "autoBuyerUpgrade", "autobuyMaxDimboosts",
|
|
"ipOffline"]
|
|
);
|
|
player.infinityRebuyables = [8, 7, 10];
|
|
} else if (EternityMilestone.keepInfinityUpgrades.isReached) {
|
|
player.infinityUpgrades = new Set(
|
|
["timeMult", "dimMult", "timeMult2",
|
|
"skipReset1", "skipReset2", "unspentBonus",
|
|
"27Mult", "18Mult", "36Mult", "resetMult",
|
|
"skipReset3", "passiveGen", "45Mult",
|
|
"resetBoost", "galaxyBoost", "skipResetGalaxy",
|
|
"ipOffline"]
|
|
);
|
|
player.infinityRebuyables = [0, 0, 0];
|
|
} else {
|
|
player.infinityUpgrades.clear();
|
|
player.infinityRebuyables = [0, 0, 0];
|
|
}
|
|
GameCache.tickSpeedMultDecrease.invalidate();
|
|
GameCache.dimensionMultDecrease.invalidate();
|
|
}
|
|
|
|
function breakInfinity() {
|
|
if (!Autobuyer.bigCrunch.hasMaxedInterval) return;
|
|
if (InfinityChallenge.isRunning) return;
|
|
for (const autobuyer of Autobuyers.all) {
|
|
if (autobuyer.data.interval !== undefined) autobuyer.maxIntervalForFree();
|
|
}
|
|
player.break = !player.break;
|
|
EventHub.dispatch(player.break ? GAME_EVENT.BREAK_INFINITY : GAME_EVENT.FIX_INFINITY);
|
|
GameUI.update();
|
|
}
|
|
|
|
function gainedInfinityPoints() {
|
|
const div = Effects.min(
|
|
308,
|
|
Achievement(103),
|
|
TimeStudy(111)
|
|
);
|
|
let ip = player.break
|
|
? Decimal.pow10(player.records.thisInfinity.maxAM.log10() / div - 0.75)
|
|
: new Decimal(308 / div);
|
|
if (Effarig.isRunning && Effarig.currentStage === EFFARIG_STAGES.ETERNITY) {
|
|
ip = ip.min(1e200);
|
|
}
|
|
ip = ip.times(GameCache.totalIPMult.value);
|
|
if (Teresa.isRunning) {
|
|
ip = ip.pow(0.55);
|
|
} else if (V.isRunning) {
|
|
ip = ip.pow(0.5);
|
|
} else if (Laitela.isRunning) {
|
|
ip = dilatedValueOf(ip);
|
|
}
|
|
if (GlyphAlteration.isAdded("infinity")) {
|
|
ip = ip.pow(getSecondaryGlyphEffect("infinityIP"));
|
|
}
|
|
return ip.floor();
|
|
}
|
|
|
|
function totalEPMult() {
|
|
return new Decimal(getAdjustedGlyphEffect("cursedEP"))
|
|
.times(ShopPurchase.EPPurchases.currentMult)
|
|
.timesEffectsOf(
|
|
EternityUpgrade.epMult,
|
|
TimeStudy(61),
|
|
TimeStudy(122),
|
|
TimeStudy(121),
|
|
TimeStudy(123),
|
|
RealityUpgrade(12),
|
|
GlyphEffect.epMult
|
|
);
|
|
}
|
|
|
|
function gainedEternityPoints() {
|
|
let ep = Decimal.pow(5, Currency.infinityPoints.value.plus(
|
|
gainedInfinityPoints()).log10() / 308 - 0.7).times(totalEPMult());
|
|
|
|
if (Teresa.isRunning) {
|
|
ep = ep.pow(0.55);
|
|
} else if (V.isRunning) {
|
|
ep = ep.pow(0.5);
|
|
} else if (Laitela.isRunning) {
|
|
ep = dilatedValueOf(ep);
|
|
}
|
|
if (GlyphAlteration.isAdded("time")) {
|
|
ep = ep.pow(getSecondaryGlyphEffect("timeEP"));
|
|
}
|
|
return ep.floor();
|
|
}
|
|
|
|
function requiredIPForEP(epAmount) {
|
|
return Decimal.pow10(308 * (Decimal.log(totalEPMult().dividedBy(epAmount).reciprocal(), 5) + 0.7))
|
|
.clampMin(Number.MAX_VALUE);
|
|
}
|
|
|
|
function gainedGlyphLevel() {
|
|
const glyphState = getGlyphLevelInputs();
|
|
let rawLevel = Math.floor(glyphState.rawLevel);
|
|
if (!isFinite(rawLevel)) rawLevel = 0;
|
|
let actualLevel = Math.floor(glyphState.actualLevel);
|
|
if (!isFinite(actualLevel)) actualLevel = 0;
|
|
return {
|
|
rawLevel,
|
|
actualLevel
|
|
};
|
|
}
|
|
|
|
function resetChallengeStuff() {
|
|
player.chall2Pow = 1;
|
|
player.chall3Pow = new Decimal(0.01);
|
|
player.matter = new Decimal(0);
|
|
player.chall8TotalSacrifice = new Decimal(1);
|
|
player.postC4Tier = 1;
|
|
}
|
|
|
|
function ratePerMinute(amount, time) {
|
|
return Decimal.divide(amount, time / (60 * 1000));
|
|
}
|
|
|
|
function averageRun(runs, name) {
|
|
const totalTime = runs.map(run => run[0]).sum();
|
|
const totalAmount = runs
|
|
.map(run => run[1])
|
|
.reduce(Decimal.sumReducer);
|
|
const totalPrestigeGain = runs
|
|
.map(run => run[2])
|
|
.reduce(name === "Reality" ? Number.sumReducer : Decimal.sumReducer);
|
|
const realTime = runs.map(run => run[3]).sum();
|
|
const average = [
|
|
totalTime / runs.length,
|
|
totalAmount.dividedBy(runs.length),
|
|
(name === "Reality") ? totalPrestigeGain / runs.length : totalPrestigeGain.dividedBy(runs.length),
|
|
realTime / runs.length
|
|
];
|
|
if (name === "Reality") {
|
|
average.push(runs.map(x => x[4]).sum() / runs.length);
|
|
}
|
|
return average;
|
|
}
|
|
|
|
// eslint-disable-next-line max-params
|
|
function addInfinityTime(time, realTime, ip, infinities) {
|
|
player.records.lastTenInfinities.pop();
|
|
player.records.lastTenInfinities.unshift([time, ip, infinities, realTime]);
|
|
GameCache.bestRunIPPM.invalidate();
|
|
}
|
|
|
|
function resetInfinityRuns() {
|
|
player.records.lastTenInfinities = Array.from(
|
|
{ length: 10 },
|
|
() => [Number.MAX_VALUE, new Decimal(1), new Decimal(1), Number.MAX_VALUE]
|
|
);
|
|
GameCache.bestRunIPPM.invalidate();
|
|
}
|
|
|
|
// Player gains 50% of infinities they would get based on their best infinities/hour crunch if they have the
|
|
// milestone and turned on infinity autobuyer with 1 minute or less per crunch
|
|
function getInfinitiedMilestoneReward(ms, considerMilestoneReached) {
|
|
return Autobuyer.bigCrunch.autoInfinitiesAvailable(considerMilestoneReached)
|
|
? Decimal.floor(player.records.thisEternity.bestInfinitiesPerMs.times(ms).dividedBy(2))
|
|
: new Decimal(0);
|
|
}
|
|
|
|
// eslint-disable-next-line max-params
|
|
function addEternityTime(time, realTime, ep, eternities) {
|
|
player.records.lastTenEternities.pop();
|
|
player.records.lastTenEternities.unshift([time, ep, eternities, realTime]);
|
|
GameCache.averageRealTimePerEternity.invalidate();
|
|
}
|
|
|
|
function resetEternityRuns() {
|
|
player.records.lastTenEternities = Array.from(
|
|
{ length: 10 },
|
|
() => [Number.MAX_VALUE, new Decimal(1), new Decimal(1), Number.MAX_VALUE]
|
|
);
|
|
GameCache.averageRealTimePerEternity.invalidate();
|
|
}
|
|
|
|
// Player gains 50% of the eternities they would get if they continuously repeated their fastest eternity, if they
|
|
// have the auto-eternity milestone and turned on eternity autobuyer with 0 EP
|
|
function getEternitiedMilestoneReward(ms, considerMilestoneReached) {
|
|
return Autobuyer.eternity.autoEternitiesAvailable(considerMilestoneReached)
|
|
? Decimal.floor(player.records.thisReality.bestEternitiesPerMs.times(ms).dividedBy(2))
|
|
: new Decimal(0);
|
|
}
|
|
|
|
function isOfflineEPGainEnabled() {
|
|
return !Autobuyer.bigCrunch.autoInfinitiesAvailable() &&
|
|
!Autobuyer.eternity.autoEternitiesAvailable();
|
|
}
|
|
|
|
function getOfflineEPGain(ms) {
|
|
if (!EternityMilestone.autoEP.isReached || !isOfflineEPGainEnabled()) return new Decimal(0);
|
|
return player.records.bestEternity.bestEPminReality.times(TimeSpan.fromMilliseconds(ms).totalMinutes / 4);
|
|
}
|
|
|
|
// eslint-disable-next-line max-params
|
|
function addRealityTime(time, realTime, rm, level, realities) {
|
|
player.records.lastTenRealities.pop();
|
|
player.records.lastTenRealities.unshift([time, rm, realities, realTime, level]);
|
|
}
|
|
|
|
function gainedInfinities() {
|
|
if (EternityChallenge(4).isRunning) {
|
|
return new Decimal(1);
|
|
}
|
|
let infGain = Effects.max(
|
|
1,
|
|
Achievement(87)
|
|
).toDecimal();
|
|
|
|
infGain = infGain.timesEffectsOf(
|
|
TimeStudy(32),
|
|
RealityUpgrade(5),
|
|
RealityUpgrade(7),
|
|
Achievement(164)
|
|
);
|
|
infGain = infGain.times(getAdjustedGlyphEffect("infinityinfmult"));
|
|
infGain = infGain.times(RA_UNLOCKS.TT_BOOST.effect.infinity());
|
|
infGain = infGain.powEffectOf(SingularityMilestone.infinitiedPow);
|
|
return infGain;
|
|
}
|
|
|
|
// TODO: remove before release
|
|
(function() {
|
|
if (isLocalEnvironment()) return;
|
|
let commit;
|
|
setInterval(() => {
|
|
const url = "https://api.github.com/repos/IvarK/IToughtAboutCurseWordsButThatWouldBeMeanToOmsi/commits/master";
|
|
const headers = new Headers();
|
|
// Yes, this is my GitHub API key for reading private repo details
|
|
headers.append("Authorization", `Basic ${btoa("WaitingIdly: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")}`);
|
|
|
|
fetch(url, { method: "GET", headers })
|
|
.then(response => response.json())
|
|
.then(json => {
|
|
if (commit === undefined) {
|
|
commit = json.sha;
|
|
return;
|
|
}
|
|
if (commit === json.sha) return;
|
|
// GH Pages need some time to get rebuilt, so show message after 30 seconds
|
|
setTimeout(() => {
|
|
Modal.message.show(
|
|
"Refresh the page (game will be saved), we've got new stuff: " +
|
|
`"${json.commit.message}" by ${json.author.login}`,
|
|
updateRefresh,
|
|
true
|
|
);
|
|
}, 30000);
|
|
});
|
|
}, 60000);
|
|
}());
|
|
|
|
function updateRefresh() {
|
|
GameStorage.save(true);
|
|
location.reload(true);
|
|
}
|
|
|
|
const GAME_SPEED_EFFECT = {
|
|
FIXED_SPEED: 1,
|
|
TIME_GLYPH: 2,
|
|
BLACK_HOLE: 3,
|
|
TIME_STORAGE: 4,
|
|
SINGULARITY_MILESTONE: 5,
|
|
NERFS: 6
|
|
};
|
|
|
|
/**
|
|
* @param {number[]} effectsToConsider A list of various game speed changing effects to apply when calculating
|
|
* the game speed. If left undefined, all effects will be applied.
|
|
* @param {number} blackHolesActiveOverride A numerical value which forces all black holes up to its specified index
|
|
* to be active for the purposes of game speed calculation. This is only used during offline black hole stuff.
|
|
*/
|
|
function getGameSpeedupFactor(effectsToConsider, blackHolesActiveOverride) {
|
|
let effects;
|
|
if (effectsToConsider === undefined) {
|
|
effects = [GAME_SPEED_EFFECT.FIXED_SPEED, GAME_SPEED_EFFECT.TIME_GLYPH, GAME_SPEED_EFFECT.BLACK_HOLE,
|
|
GAME_SPEED_EFFECT.TIME_STORAGE, GAME_SPEED_EFFECT.SINGULARITY_MILESTONE, GAME_SPEED_EFFECT.NERFS];
|
|
} else {
|
|
effects = effectsToConsider;
|
|
}
|
|
|
|
if (effects.includes(GAME_SPEED_EFFECT.FIXED_SPEED)) {
|
|
if (EternityChallenge(12).isRunning) {
|
|
return 1 / 1000;
|
|
}
|
|
}
|
|
|
|
let factor = 1;
|
|
if (effects.includes(GAME_SPEED_EFFECT.BLACK_HOLE)) {
|
|
if (BlackHoles.arePaused) {
|
|
factor *= player.blackHoleNegative;
|
|
} else {
|
|
for (const blackHole of BlackHoles.list) {
|
|
if (!blackHole.isUnlocked) break;
|
|
const isActive = blackHolesActiveOverride === undefined
|
|
? blackHole.isActive
|
|
: blackHole.id <= blackHolesActiveOverride;
|
|
if (!isActive) break;
|
|
factor *= Math.pow(blackHole.power, BlackHoles.unpauseAccelerationFactor);
|
|
if (V.has(V_UNLOCKS.ACHIEVEMENT_BH)) {
|
|
factor *= V_UNLOCKS.ACHIEVEMENT_BH.effect();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (effects.includes(GAME_SPEED_EFFECT.SINGULARITY_MILESTONE)) {
|
|
factor *= SingularityMilestone.gamespeedFromSingularities.canBeApplied
|
|
? SingularityMilestone.gamespeedFromSingularities.effectValue
|
|
: 1;
|
|
}
|
|
|
|
if (effects.includes(GAME_SPEED_EFFECT.TIME_GLYPH)) {
|
|
factor *= getAdjustedGlyphEffect("timespeed");
|
|
factor = Math.pow(factor, getAdjustedGlyphEffect("effarigblackhole"));
|
|
}
|
|
|
|
// Time storage is linearly scaled because exponential scaling is pretty useless in practice
|
|
if (Enslaved.isStoringGameTime && effects.includes(GAME_SPEED_EFFECT.TIME_STORAGE)) {
|
|
const storedTimeWeight = player.celestials.enslaved.storedFraction;
|
|
factor = factor * (1 - storedTimeWeight) + storedTimeWeight;
|
|
}
|
|
|
|
// These effects should always be active, but need to be disabled during offline black hole simulations because
|
|
// otherwise it gets applied twice
|
|
if (effects.includes(GAME_SPEED_EFFECT.NERFS)) {
|
|
if (Effarig.isRunning) {
|
|
factor = Effarig.multiplier(factor).toNumber();
|
|
} else if (Laitela.isRunning) {
|
|
const nerfModifier = Math.clampMax(Time.thisRealityRealTime.totalMinutes / 10, 1);
|
|
factor = Math.pow(factor, nerfModifier);
|
|
}
|
|
}
|
|
|
|
// 1e-300 is now possible with max inverted BH, going below it would be possible with
|
|
// an effarig glyph.
|
|
factor = Math.clamp(factor, 1e-300, 1e300);
|
|
|
|
// Dev speedup should always be active
|
|
if (tempSpeedupToggle) {
|
|
factor *= tempSpeedupFactor;
|
|
}
|
|
return factor;
|
|
}
|
|
|
|
function getGameSpeedupForDisplay() {
|
|
const speedFactor = getGameSpeedupFactor();
|
|
if (Enslaved.isAutoReleasing && Enslaved.canRelease(true) && !BlackHoles.areNegative) {
|
|
return Math.max(Enslaved.autoReleaseSpeed, speedFactor);
|
|
}
|
|
return speedFactor;
|
|
}
|
|
|
|
// "diff" is in ms. It is only unspecified when it's being called normally and not due to simulating time, in which
|
|
// case it uses the gap between now and the last time the function was called. This is on average equal to the update
|
|
// rate.
|
|
// TODO: Clean this up, remove the disable line
|
|
// eslint-disable-next-line complexity
|
|
function gameLoop(passDiff, options = {}) {
|
|
let diff = passDiff;
|
|
PerformanceStats.start("Frame Time");
|
|
PerformanceStats.start("Game Update");
|
|
EventHub.dispatch(GAME_EVENT.GAME_TICK_BEFORE);
|
|
const thisUpdate = Date.now();
|
|
const realDiff = diff === undefined
|
|
? Math.clamp(thisUpdate - player.lastUpdate, 1, 21600000)
|
|
: diff;
|
|
|
|
// Ra memory generation bypasses stored real time, but memory chunk generation is disabled when storing real time.
|
|
// This is in order to prevent players from using time inside of Ra's reality for amplification as well
|
|
Ra.memoryTick(realDiff, !Enslaved.isStoringRealTime);
|
|
if (AlchemyResource.momentum.isUnlocked) player.celestials.ra.momentumTime += realDiff;
|
|
|
|
// Lai'tela mechanics should bypass stored real time entirely
|
|
Laitela.tickDarkMatter(realDiff);
|
|
Laitela.autobuyerLoop(realDiff);
|
|
|
|
// When storing real time, skip everything else having to do with production once stats are updated
|
|
if (Enslaved.isStoringRealTime) {
|
|
player.records.realTimePlayed += realDiff;
|
|
player.records.thisInfinity.realTime += realDiff;
|
|
player.records.thisEternity.realTime += realDiff;
|
|
player.records.thisReality.realTime += realDiff;
|
|
Enslaved.storeRealTime();
|
|
GameUI.update();
|
|
return;
|
|
}
|
|
|
|
// Ra-Enslaved auto-release stored time (once every 5 ticks)
|
|
if (Enslaved.isAutoReleasing) {
|
|
Enslaved.autoReleaseTick++;
|
|
}
|
|
if (Enslaved.autoReleaseTick >= 5) {
|
|
Enslaved.autoReleaseTick = 0;
|
|
Enslaved.useStoredTime(true);
|
|
Enslaved.isReleaseTick = true;
|
|
} else if (!Enslaved.isReleaseTick) {
|
|
Enslaved.nextTickDiff = realDiff;
|
|
}
|
|
if (diff === undefined) {
|
|
diff = Enslaved.nextTickDiff;
|
|
}
|
|
|
|
Autobuyers.tick();
|
|
Tutorial.tutorialLoop();
|
|
|
|
if (Achievement(165).isUnlocked && player.celestials.effarig.autoAdjustGlyphWeights) {
|
|
autoAdjustGlyphWeights();
|
|
}
|
|
|
|
// We do these after autobuyers, since it's possible something there might
|
|
// change a multiplier.
|
|
GameCache.antimatterDimensionCommonMultiplier.invalidate();
|
|
GameCache.antimatterDimensionFinalMultipliers.invalidate();
|
|
GameCache.infinityDimensionCommonMultiplier.invalidate();
|
|
GameCache.timeDimensionCommonMultiplier.invalidate();
|
|
GameCache.totalIPMult.invalidate();
|
|
|
|
const blackHoleDiff = realDiff;
|
|
const fixedSpeedActive = EternityChallenge(12).isRunning;
|
|
if (!Enslaved.isReleaseTick && !fixedSpeedActive) {
|
|
let speedFactor;
|
|
if (options.blackHoleSpeedup === undefined) {
|
|
speedFactor = getGameSpeedupFactor();
|
|
} else {
|
|
// This is only called from simulateTime() and is calculated externally in order to avoid weirdness when game
|
|
// speed is directly nerfed
|
|
speedFactor = options.blackHoleSpeedup;
|
|
}
|
|
|
|
if (Enslaved.isStoringGameTime && !fixedSpeedActive) {
|
|
// These variables are the actual game speed used and the game speed unaffected by time storage, respectively
|
|
const reducedTimeFactor = getGameSpeedupFactor();
|
|
const totalTimeFactor = getGameSpeedupFactor([GAME_SPEED_EFFECT.FIXED_SPEED, GAME_SPEED_EFFECT.TIME_GLYPH,
|
|
GAME_SPEED_EFFECT.BLACK_HOLE, GAME_SPEED_EFFECT.SINGULARITY_MILESTONE]);
|
|
const amplification = Ra.has(RA_UNLOCKS.IMPROVED_STORED_TIME)
|
|
? RA_UNLOCKS.IMPROVED_STORED_TIME.effect.gameTimeAmplification()
|
|
: 1;
|
|
Enslaved.currentBlackHoleStoreAmountPerMs = (totalTimeFactor - reducedTimeFactor) * amplification;
|
|
player.celestials.enslaved.stored = Math.clampMax(player.celestials.enslaved.stored +
|
|
diff * Enslaved.currentBlackHoleStoreAmountPerMs, Enslaved.timeCap);
|
|
speedFactor = reducedTimeFactor;
|
|
}
|
|
diff *= speedFactor;
|
|
} else if (fixedSpeedActive) {
|
|
diff *= getGameSpeedupFactor();
|
|
}
|
|
player.celestials.ra.peakGamespeed = Math.max(player.celestials.ra.peakGamespeed, getGameSpeedupFactor());
|
|
Enslaved.isReleaseTick = false;
|
|
|
|
// These need to all be done consecutively in order to minimize the chance of a reset occurring between real time
|
|
// updating and game time updating. This is only particularly noticeable when game speed is 1 and the player
|
|
// expects to see identical numbers.
|
|
player.records.realTimePlayed += realDiff;
|
|
player.records.totalTimePlayed += diff;
|
|
player.records.thisInfinity.realTime += realDiff;
|
|
player.records.thisInfinity.time += diff;
|
|
player.records.thisEternity.realTime += realDiff;
|
|
if (Enslaved.isRunning && Enslaved.feltEternity && !EternityChallenge(12).isRunning) {
|
|
player.records.thisEternity.time += diff * (1 + Currency.eternities.value.clampMax(1e66).toNumber());
|
|
} else {
|
|
player.records.thisEternity.time += diff;
|
|
}
|
|
player.records.thisReality.realTime += realDiff;
|
|
player.records.thisReality.time += diff;
|
|
|
|
DeltaTimeState.update(realDiff, diff);
|
|
|
|
updateNormalAndInfinityChallenges(diff);
|
|
|
|
// IP generation is broken into a couple of places in gameLoop; changing that might change the
|
|
// behavior of eternity farming.
|
|
preProductionGenerateIP(diff);
|
|
|
|
let eternitiedGain = 0;
|
|
if (RealityUpgrade(14).isBought) {
|
|
eternitiedGain = Effects.product(
|
|
RealityUpgrade(3),
|
|
RealityUpgrade(14)
|
|
);
|
|
eternitiedGain = Decimal.times(eternitiedGain, getAdjustedGlyphEffect("timeetermult"));
|
|
eternitiedGain = new Decimal(Time.deltaTime).times(
|
|
Decimal.pow(eternitiedGain, AlchemyResource.eternity.effectValue));
|
|
player.reality.partEternitied = player.reality.partEternitied.plus(eternitiedGain);
|
|
Currency.eternities.add(player.reality.partEternitied.floor());
|
|
player.reality.partEternitied = player.reality.partEternitied.sub(player.reality.partEternitied.floor());
|
|
}
|
|
|
|
if (!EternityChallenge(4).isRunning) {
|
|
let infGen = new Decimal(0);
|
|
if (BreakInfinityUpgrade.infinitiedGen.isBought) {
|
|
// Multipliers are done this way to explicitly exclude ach87 and TS32
|
|
infGen = infGen.plus(0.2 * Time.deltaTimeMs / player.records.bestInfinity.time);
|
|
infGen = infGen.timesEffectsOf(
|
|
RealityUpgrade(5),
|
|
RealityUpgrade(7)
|
|
);
|
|
infGen = infGen.times(getAdjustedGlyphEffect("infinityinfmult"));
|
|
infGen = infGen.times(RA_UNLOCKS.TT_BOOST.effect.infinity());
|
|
}
|
|
if (RealityUpgrade(11).isBought) {
|
|
infGen = infGen.plus(RealityUpgrade(11).effectValue.times(Time.deltaTime));
|
|
}
|
|
if (EffarigUnlock.eternity.isUnlocked) {
|
|
// We consider half of the eternities we gained above this tick
|
|
// to have been gained before the infinities, and thus not to
|
|
// count here. This gives us the desirable behavior that
|
|
// infinities and eternities gained overall will be the same
|
|
// for two ticks as for one tick of twice the length.
|
|
infGen = infGen.plus(gainedInfinities().times(
|
|
Currency.eternities.value.minus(eternitiedGain.div(2).floor())).times(Time.deltaTime));
|
|
}
|
|
infGen = infGen.plus(player.partInfinitied);
|
|
Currency.infinities.add(infGen.floor());
|
|
player.partInfinitied = infGen.minus(infGen.floor()).toNumber();
|
|
}
|
|
|
|
applyAutoprestige(realDiff);
|
|
updateImaginaryMachines(realDiff);
|
|
|
|
const uncountabilityGain = AlchemyResource.uncountability.effectValue * Time.unscaledDeltaTime.totalSeconds;
|
|
Currency.realities.add(uncountabilityGain);
|
|
Currency.perkPoints.add(uncountabilityGain);
|
|
|
|
if (Perk.autocompleteEC1.isBought && player.reality.autoEC) player.reality.lastAutoEC += realDiff;
|
|
|
|
EternityChallenge(12).tryFail();
|
|
Achievements._power.invalidate();
|
|
|
|
TimeDimensions.tick(diff);
|
|
InfinityDimensions.tick(diff);
|
|
AntimatterDimensions.tick(diff);
|
|
|
|
const gain = Math.clampMin(FreeTickspeed.fromShards(Currency.timeShards.value).newAmount - player.totalTickGained, 0);
|
|
player.totalTickGained += gain;
|
|
|
|
const currentIPmin = gainedInfinityPoints().dividedBy(Time.thisInfinityRealTime.totalMinutes);
|
|
if (currentIPmin.gt(player.records.thisInfinity.bestIPmin) && Player.canCrunch)
|
|
player.records.thisInfinity.bestIPmin = currentIPmin;
|
|
|
|
tryUnlockInfinityChallenges();
|
|
|
|
EternityChallenges.autoComplete.tick();
|
|
|
|
replicantiLoop(diff);
|
|
|
|
|
|
const currentEPmin = gainedEternityPoints().dividedBy(Time.thisEternityRealTime.totalMinutes);
|
|
if (currentEPmin.gt(player.records.thisEternity.bestEPmin) && Player.canEternity)
|
|
player.records.thisEternity.bestEPmin = currentEPmin;
|
|
|
|
if (PlayerProgress.dilationUnlocked()) {
|
|
Currency.dilatedTime.add(getDilationGainPerSecond().times(diff / 1000));
|
|
}
|
|
|
|
updateTachyonGalaxies();
|
|
Currency.timeTheorems.add(getTTPerSecond().times(diff / 1000));
|
|
tryUnlockInfinityDimensions(true);
|
|
|
|
BlackHoles.updatePhases(blackHoleDiff);
|
|
|
|
// Code to auto-unlock dilation; 16617 is the cost for buying literally all time studies and unlocking dilation
|
|
if (Ra.has(RA_UNLOCKS.INSTANT_AUTOEC) && Currency.timeTheorems.max.gte(16617)) {
|
|
TimeStudy.dilation.purchase(true);
|
|
}
|
|
|
|
applyAutoUnlockPerks();
|
|
if (GlyphSelection.active) GlyphSelection.update(gainedGlyphLevel());
|
|
|
|
if (player.dilation.active && Ra.has(RA_UNLOCKS.AUTO_TP)) rewardTP();
|
|
|
|
if (!EnslavedProgress.hintsUnlocked.hasProgress && Enslaved.has(ENSLAVED_UNLOCKS.RUN) && !Enslaved.isCompleted) {
|
|
player.celestials.enslaved.hintUnlockProgress += Enslaved.isRunning ? realDiff : realDiff / 25;
|
|
if (player.celestials.enslaved.hintUnlockProgress >= TimeSpan.fromHours(5).totalMilliseconds) {
|
|
EnslavedProgress.hintsUnlocked.giveProgress();
|
|
Enslaved.quotes.show(Enslaved.quotes.HINT_UNLOCK);
|
|
}
|
|
}
|
|
|
|
laitelaRealityTick(realDiff);
|
|
Achievements.autoAchieveUpdate(diff);
|
|
V.checkForUnlocks();
|
|
Ra.updateAlchemyFlow();
|
|
AutomatorBackend.update(realDiff);
|
|
|
|
EventHub.dispatch(GAME_EVENT.GAME_TICK_AFTER);
|
|
GameUI.update();
|
|
player.lastUpdate = thisUpdate;
|
|
PerformanceStats.end("Game Update");
|
|
}
|
|
|
|
// Applies all perks which automatically unlock things when passing certain thresholds, needs to be checked every tick
|
|
function applyAutoUnlockPerks() {
|
|
if (!TimeDimension(8).isUnlocked && Perk.autounlockTD.isBought) {
|
|
for (let dim = 5; dim <= 8; ++dim) TimeStudy.timeDimension(dim).purchase();
|
|
}
|
|
if (Perk.autounlockDilation3.isBought) buyDilationUpgrade(DilationUpgrade.ttGenerator.id);
|
|
if (Perk.autounlockReality.isBought) TimeStudy.reality.purchase(true);
|
|
if (player.eternityUpgrades.size < 6 && Perk.autounlockEU2.isBought) {
|
|
const secondRow = Object.values(EternityUpgrade).filter(u => u.id > 3);
|
|
for (const upgrade of secondRow) {
|
|
if (player.eternityPoints.gte(upgrade.cost / 1e10)) player.eternityUpgrades.add(upgrade.id);
|
|
}
|
|
}
|
|
}
|
|
|
|
function laitelaRealityTick(realDiff) {
|
|
const laitelaInfo = player.celestials.laitela;
|
|
if (!Laitela.isRunning) return;
|
|
if (laitelaInfo.entropy >= 0) {
|
|
laitelaInfo.entropy += (realDiff / 1000) * Laitela.entropyGainPerSecond;
|
|
}
|
|
|
|
// Setting entropy to -1 on completion prevents the modal from showing up repeatedly
|
|
if (laitelaInfo.entropy >= 1) {
|
|
let completionText = `Lai'tela's Reality has been destabilized after ${Time.thisRealityRealTime.toStringShort()}.`;
|
|
laitelaInfo.entropy = -1;
|
|
const oldInfo = {
|
|
fastestCompletion: laitelaInfo.fastestCompletion,
|
|
difficultyTier: laitelaInfo.difficultyTier,
|
|
realityReward: Laitela.realityReward
|
|
};
|
|
laitelaInfo.thisCompletion = Time.thisRealityRealTime.totalSeconds;
|
|
laitelaInfo.fastestCompletion = Math.min(laitelaInfo.thisCompletion, laitelaInfo.fastestCompletion);
|
|
clearCelestialRuns();
|
|
if (Time.thisRealityRealTime.totalSeconds < 30) {
|
|
laitelaInfo.difficultyTier++;
|
|
laitelaInfo.fastestCompletion = 300;
|
|
completionText += laitelaBeatText(Laitela.maxAllowedDimension + 1);
|
|
}
|
|
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)})`;
|
|
player.records.bestReality.laitelaSet = Glyphs.copyForRecords(Glyphs.active.filter(g => g !== null));
|
|
} else {
|
|
completionText += ` You need to destabilize in faster than
|
|
${TimeSpan.fromSeconds(laitelaInfo.fastestCompletion).toStringShort()} to improve your multiplier.`;
|
|
}
|
|
Modal.message.show(completionText);
|
|
}
|
|
}
|
|
|
|
function laitelaBeatText(disabledDim) {
|
|
switch (disabledDim) {
|
|
case 1: return `<br><br>Lai'tela's Reality will now completely disable production from all Dimensions.
|
|
The Reality can still be entered, but further destabilization is no longer possible.
|
|
For completely destabilizing the Reality, you also get an additional ${formatX(8)} to Dark Energy gain.`;
|
|
case 2:
|
|
case 3: return `<br><br>Lai'tela's Reality will now disable production from all
|
|
${disabledDim}${disabledDim === 2 ? "nd" : "rd"} Dimensions during
|
|
future runs, but the reward will be ${formatInt(100)} times stronger than before.`;
|
|
case 8: return `<br><br>Lai'tela's Reality will now disable production from all 8th Dimensions during
|
|
future runs, but the reward will be ${formatInt(100)} times stronger than before. This boost can be
|
|
repeated for each remaining Dimension by reaching destabilization within ${formatInt(30)} seconds again.`;
|
|
default: return `<br><br>Lai'tela's Reality will now disable production from all
|
|
${disabledDim}th Dimensions during future runs, but the reward will be
|
|
${formatInt(100)} times stronger than before.`;
|
|
}
|
|
}
|
|
|
|
// This gives IP/EP/RM from the respective upgrades that reward the prestige currencies continuously
|
|
function applyAutoprestige(diff) {
|
|
Currency.infinityPoints.add(TimeStudy(181).effectOrDefault(0));
|
|
|
|
if (Teresa.has(TERESA_UNLOCKS.EPGEN)) {
|
|
Currency.eternityPoints.add(player.records.thisEternity.bestEPmin.times(0.01)
|
|
.times(getGameSpeedupFactor() * diff / 1000).times(RA_UNLOCKS.TT_BOOST.effect.autoPrestige()));
|
|
}
|
|
|
|
if (InfinityUpgrade.ipGen.isCharged) {
|
|
const addedRM = MachineHandler.gainedRealityMachines
|
|
.timesEffectsOf(InfinityUpgrade.ipGen.chargedEffect)
|
|
.times(diff / 1000);
|
|
Currency.realityMachines.add(addedRM);
|
|
}
|
|
}
|
|
|
|
function updateImaginaryMachines(diff) {
|
|
MachineHandler.updateIMCap();
|
|
Currency.imaginaryMachines.add(MachineHandler.gainedImaginaryMachines(diff));
|
|
}
|
|
|
|
function updateTachyonGalaxies() {
|
|
const tachyonGalaxyMult = Effects.max(1, DilationUpgrade.doubleGalaxies);
|
|
const tachyonGalaxyThreshold = 1000;
|
|
const thresholdMult = getTachyonGalaxyMult();
|
|
player.dilation.baseTachyonGalaxies = Math.max(player.dilation.baseTachyonGalaxies,
|
|
1 + Math.floor(Decimal.log(Currency.dilatedTime.value.dividedBy(1000), thresholdMult)));
|
|
player.dilation.nextThreshold = new Decimal(1000).times(new Decimal(thresholdMult)
|
|
.pow(player.dilation.baseTachyonGalaxies));
|
|
player.dilation.totalTachyonGalaxies =
|
|
Math.min(player.dilation.baseTachyonGalaxies * tachyonGalaxyMult, tachyonGalaxyThreshold) +
|
|
Math.max(player.dilation.baseTachyonGalaxies * tachyonGalaxyMult - tachyonGalaxyThreshold, 0) / tachyonGalaxyMult;
|
|
}
|
|
|
|
function getTTPerSecond() {
|
|
// All TT multipliers (note that this is equal to 1 pre-Ra)
|
|
let ttMult = RA_UNLOCKS.TT_BOOST.effect.ttGen();
|
|
ttMult *= Achievement(137).effectOrDefault(1);
|
|
if (Ra.has(RA_UNLOCKS.TT_ACHIEVEMENT)) ttMult *= RA_UNLOCKS.TT_ACHIEVEMENT.effect();
|
|
if (GlyphAlteration.isAdded("dilation")) ttMult *= getSecondaryGlyphEffect("dilationTTgen");
|
|
|
|
// Glyph TT generation
|
|
const glyphTT = Teresa.isRunning || Enslaved.isRunning
|
|
? 0
|
|
: getAdjustedGlyphEffect("dilationTTgen") * ttMult;
|
|
|
|
// Dilation TT generation
|
|
const dilationTT = DilationUpgrade.ttGenerator.isBought
|
|
? DilationUpgrade.ttGenerator.effectValue.times(ttMult)
|
|
: new Decimal(0);
|
|
|
|
// Lai'tela TT power
|
|
let finalTT = dilationTT.add(glyphTT);
|
|
if (SingularityMilestone.theoremPowerFromSingularities.isUnlocked && finalTT.gt(1)) {
|
|
finalTT = finalTT.pow(SingularityMilestone.theoremPowerFromSingularities.effectValue);
|
|
}
|
|
|
|
return finalTT;
|
|
}
|
|
|
|
function recursiveTimeOut(fn, iterations, endFn) {
|
|
fn(iterations);
|
|
if (iterations === 0) endFn();
|
|
else setTimeout(() => recursiveTimeOut(fn, iterations - 1, endFn), 0);
|
|
}
|
|
|
|
function afterSimulation(seconds, playerBefore) {
|
|
if (seconds > 600) {
|
|
const playerAfter = deepmerge.all([{}, player]);
|
|
Modal.awayProgress.show({ playerBefore, playerAfter, seconds });
|
|
}
|
|
|
|
GameUI.notify.showBlackHoles = true;
|
|
}
|
|
|
|
function simulateTime(seconds, real, fast) {
|
|
// The game is simulated at a base 50ms update rate, with a max of
|
|
// player.options.offlineTicks ticks. additional ticks are converted
|
|
// into a higher diff per tick
|
|
// warning: do not call this function with real unless you know what you're doing
|
|
// calling it with fast will only simulate it with a max of 50 ticks
|
|
let ticks = Math.floor(seconds * 20);
|
|
GameUI.notify.showBlackHoles = false;
|
|
|
|
// Limit the tick count (this also applies if the black hole is unlocked)
|
|
if (ticks > player.options.offlineTicks && !real && !fast) {
|
|
ticks = player.options.offlineTicks;
|
|
} else if (ticks > 50 && fast) {
|
|
ticks = 50;
|
|
}
|
|
// 1000 * seconds is milliseconds so 1000 * seconds / ticks is milliseconds per tick.
|
|
const largeDiff = 1000 * seconds / ticks;
|
|
|
|
const playerStart = deepmerge.all([{}, player]);
|
|
|
|
let totalGameTime;
|
|
|
|
if (BlackHoles.areUnlocked && !BlackHoles.arePaused) {
|
|
totalGameTime = BlackHoles.calculateGameTimeFromRealTime(seconds, BlackHoles.calculateSpeedups());
|
|
} else {
|
|
totalGameTime = getGameSpeedupFactor() * seconds;
|
|
}
|
|
|
|
const infinitiedMilestone = getInfinitiedMilestoneReward(totalGameTime * 1000);
|
|
const eternitiedMilestone = getEternitiedMilestoneReward(totalGameTime * 1000);
|
|
|
|
if (eternitiedMilestone.gt(0)) {
|
|
Currency.eternities.add(eternitiedMilestone);
|
|
} else if (infinitiedMilestone.gt(0)) {
|
|
Currency.infinities.add(infinitiedMilestone);
|
|
} else {
|
|
Currency.eternityPoints.add(getOfflineEPGain(totalGameTime * 1000));
|
|
}
|
|
|
|
if (InfinityUpgrade.ipOffline.isBought) {
|
|
Currency.infinityPoints.add(player.records.thisEternity.bestIPMsWithoutMaxAll.times(seconds * 1000 / 2));
|
|
}
|
|
|
|
let loopFn = () => gameLoop(largeDiff);
|
|
let remainingRealSeconds = seconds;
|
|
// Simulation code with black hole (doesn't use diff since it splits up based on real time instead)
|
|
if (BlackHoles.areUnlocked && !BlackHoles.arePaused) {
|
|
loopFn = i => {
|
|
const [realTickTime, blackHoleSpeedup] = BlackHoles.calculateOfflineTick(remainingRealSeconds,
|
|
i, 0.0001);
|
|
remainingRealSeconds -= realTickTime;
|
|
gameLoop(1000 * realTickTime, { blackHoleSpeedup });
|
|
};
|
|
}
|
|
|
|
// We don't show the offline modal here or bother with async if doing a fast simulation
|
|
if (fast) {
|
|
GameIntervals.stop();
|
|
for (let i = 0; i < 50; i++) {
|
|
loopFn();
|
|
}
|
|
GameStorage.postLoadStuff();
|
|
afterSimulation(seconds, playerStart);
|
|
} else {
|
|
ui.view.modal.progressBar = {};
|
|
ui.view.modal.progressBar.label = "Simulating offline time...";
|
|
Async.run(loopFn,
|
|
ticks,
|
|
{
|
|
batchSize: 1,
|
|
maxTime: 60,
|
|
sleepTime: 1,
|
|
asyncEntry: doneSoFar => {
|
|
GameIntervals.stop();
|
|
ui.$viewModel.modal.progressBar = {
|
|
label: "Simulating offline time...",
|
|
current: doneSoFar,
|
|
max: ticks,
|
|
startTime: Date.now()
|
|
};
|
|
},
|
|
asyncProgress: doneSoFar => {
|
|
ui.$viewModel.modal.progressBar.current = doneSoFar;
|
|
},
|
|
asyncExit: () => {
|
|
ui.$viewModel.modal.progressBar = undefined;
|
|
// .postLoadStuff will restart GameIntervals
|
|
GameStorage.postLoadStuff();
|
|
},
|
|
then: () => {
|
|
afterSimulation(seconds, playerStart);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
window.onload = function() {
|
|
GameUI.initialized = true;
|
|
ui.view.initialized = true;
|
|
setTimeout(() => {
|
|
if (kong.enabled) {
|
|
playFabLogin();
|
|
}
|
|
document.getElementById("loading").style.display = "none";
|
|
document.body.style.overflowY = "auto";
|
|
}, 500);
|
|
};
|
|
|
|
window.onfocus = function() {
|
|
setShiftKey(false);
|
|
};
|
|
|
|
window.onblur = function() {
|
|
GameKeyboard.stopSpins();
|
|
};
|
|
|
|
function setShiftKey(isDown) {
|
|
ui.view.shiftDown = isDown;
|
|
}
|
|
|
|
function setHoldingR(x) {
|
|
Replicanti.galaxies.isPlayerHoldingR = x;
|
|
}
|
|
|
|
function init() {
|
|
// eslint-disable-next-line no-console
|
|
console.log("🌌 Antimatter Dimensions: Reality Update 🌌");
|
|
GameStorage.load();
|
|
Tabs.all.find(t => t.config.id === player.options.lastOpenTab).show(true);
|
|
kong.init();
|
|
}
|
|
|
|
init();
|
|
|
|
let tweenTime = 0;
|
|
(function() {
|
|
let lastFrame;
|
|
function animateTweens(time) {
|
|
requestAnimationFrame(animateTweens);
|
|
if (time === undefined || lastFrame === undefined) {
|
|
lastFrame = time;
|
|
return;
|
|
}
|
|
let delta = time - lastFrame;
|
|
lastFrame = time;
|
|
if (player.dilation.active) {
|
|
delta /= 10;
|
|
}
|
|
tweenTime += delta;
|
|
TWEEN.update(tweenTime);
|
|
}
|
|
|
|
animateTweens();
|
|
}());
|