pages/kronos/js/Layers/study.js

580 lines
23 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

function createCard(title, description = "", onDraw = null, modifyNextCard = null) {
return {title, description, onDraw, modifyNextCard};
}
const cardLevel = (card) => {
return (getBuyableAmount("study", card) || new Decimal(0)).add(player.study.deep);
};
const cards = {
nothing: createCard("His job is not to wield power but to draw attention away from it.", "Do nothing."),
gainPoints: createCard("Don't Panic.", level => `Successfully study ${format(getResetGain("study").times(level.add(1)))} properties.`, level => addPoints("study", getResetGain("study").times(level.add(1)))),
gainBigPoints: createCard("In his experience the Universe simply didn't work like that.", level => `Successfully study ${format(getResetGain("study").times(level.add(1)).pow(1.2))} properties. Destroy this card.`, (level, canDestroy = true) => {
addPoints("study", getResetGain("study").times(level.add(1)).pow(1.2));
if (canDestroy) {
player.study.cards.gainBigPoints = player.study.cards.gainBigPoints - 1;
}
}),
gainInsight: createCard("And it shall be called... the Earth.", level => level === 0 ? "Gain a key insight." : `Gain ${formatWhole(level.add(1))} key insights.`, level => {
player.study.insights = player.study.insights.add(level).add(1);
gainStudyXp(level.add(1).times(10));
checkJobXP("study");
}),
gainBigInsight: createCard("Yes! I shall design this computer for you.", level => `Gain ${new Decimal(getCardAmount()).times(level.add(1)).sqrt().floor()} key insights.<br/>(based on number of cards in the deck)`, level => {
const amount = new Decimal(getCardAmount()).times(level.add(1)).sqrt().floor();
player.study.insights = player.study.insights.add(amount);
gainStudyXp(amount.times(10));
checkJobXP("study");
}),
playTwice: createCard("Oh no, not again.", level => level.eq(0) ? "Play the next card twice." : `Play the next card twice, with the effect boosted by ${level.div(4)} levels.`, null, (nextCard, level) => {
if (nextCard in cards && cards[nextCard].onDraw) {
cards[nextCard].onDraw(cardLevel(nextCard).add(level.div(4)));
cards[nextCard].onDraw(cardLevel(nextCard).add(level.div(4)), false);
}
}),
increasePointsGain: createCard("Have another drink, enjoy yourself.", level => `Permanently increase studied properties gain by ${formatWhole(new Decimal(25).times(Decimal.pow(1.25, level)))}%.<br/><br/>Currently: +${formatWhole(player.study.increasePointsGain.times(new Decimal(25).times(Decimal.pow(1.25, level))))}%`, () => player.study.increasePointsGain = player.study.increasePointsGain.add(1)),
multiplyPointsGain: createCard("Reality is frequently inaccurate.", level => {
const scale = new Decimal(1.02).add(level.add(1).sqrt().div(100));
const effect = scale.pow(softcap(player.study.multiplyPointsGain, new Decimal(100).times(level.div(4).add(1)), .2));
let text = `Permanently multiply studied properties gain by x${format(scale)}<br/><br/>Currently: x${format(effect)}`;
const leftTillSoftcap = new Decimal(100).times(level.div(4).add(1)).sub(player.study.multiplyPointsGain);
if (leftTillSoftcap.gt(0)) {
text = text + "<br/>(softcapped in " + formatWhole(leftTillSoftcap) + " draws)";
} else {
text = text + "<br/>(softcapped)";
}
return text;
}, () => player.study.multiplyPointsGain = player.study.multiplyPointsGain.add(1)),
sellDiscount: createCard("It doesn't grow on trees you know.", level => {
const effect = new Decimal(0.98).pow(softcap(player.study.sellDiscount, new Decimal(100).times(level.div(2).add(1)), .5));
let text = `Permanently multiply sell cost by 0.98<br/><br/>Currently: x${formatSmall(effect)}`;
const leftTillSoftcap = new Decimal(100).times(level.div(2).add(1)).sub(player.study.sellDiscount);
if (leftTillSoftcap.gt(0)) {
text = text + "<br/>(softcapped in " + formatWhole(leftTillSoftcap) + " draws)";
} else {
text = text + "<br/>(softcapped)";
}
return text;
}, () => player.study.sellDiscount = player.study.sellDiscount.add(1)),
soldOut: createCard("Out of Stock!"),
gainXp: createCard("A billion times over ... and no one learns anything.", level => `Gain xp equal to ${level.eq(0) ? "" : `${format(level.div(4).add(1))}x times `}your number of properties.`, level => {
gainStudyXp(player.study.points.times(level.div(4).add(1)));
checkJobXP("study");
}),
increaseXpGain: createCard("There's a whole new <i>Guide</i>!", level => `Permanently increase this job's xp gain by ${formatWhole(new Decimal(10).times(Decimal.pow(1.5, level)))}%.<br/><br/>Currently: +${formatWhole(player.study.increaseXpGain.times(new Decimal(10).times(Decimal.pow(1.5, level))))}%`, () => player.study.increaseXpGain = player.study.increaseXpGain.add(1))
};
const shopCards = [
{card: "gainPoints", price: 1},
{card: "gainInsight", price: 0},
{card: "gainBigPoints", price: 8},
{card: "gainBigInsight", price: 13},
{card: "playTwice", price: 16},
{card: "increasePointsGain", price: 6},
{card: "multiplyPointsGain", price: 18},
{card: "sellDiscount", price: 14},
{card: "gainXp", price: 25},
{card: "increaseXpGain", price: 12},
];
const baseCards = () => {
return { nothing: 4, gainPoints: 4, gainInsight: 2 };
};
const getShop = (numCards = 3) => {
return new Array(numCards).fill(1).map(() => shopCards[Math.floor(Math.random() * shopCards.length)]);
};
Vue.component("card", {
props: ["layer", "data"], // data is object with card, id, className, onclick, overrideLevel, width, height, and note
template: `<div class="upgCol" v-if="data.card">
<div :id="data.id" class="card" :class="{ [data.className]: true }" :style="{ width: data.width || '200px', height: data.height || '300px' }" v-on:click="onclick">
<span style="border-bottom: 1px solid white; margin: 0; max-height: calc(50% - 30px); padding-bottom: 10px;">
<h3 v-html="title"></h3>
</span>
<span style="flex-basis: 0;"><span v-html="description"></span></span>
<span style="flex-shrink: 1"></span>
<img src="images/Time2wait.svg" alt="hourglass"/>
</div>
<div class="card-note" v-if="data.note != null">{{ data.note }}</div>
</div>`,
computed: {
title() {
return this.data.card ? isFunction(cards[this.data.card].title) ? cards[this.data.card].title(overrideLevel || cardLevel(this.data.card)) : cards[this.data.card].title : "";
},
description() {
return this.data.card ? isFunction(cards[this.data.card].description) ? cards[this.data.card].description(this.data.overrideLevel || cardLevel(this.data.card)) : cards[this.data.card].description : "";
}
},
methods: {
onclick() {
if (this.data.onclick) {
this.data.onclick();
}
}
}
});
function getCardUpgradeBuyable(id) {
const cost = x => {
const amount = x || getBuyableAmount("study", id);
return new Decimal(10).pow(amount.add(2));
};
return {
title: "Upgrade<br/>",
style: {
width: "100px",
height: "100px",
marginLeft: "10px"
},
display() {
return `Level ${formatWhole(cardLevel(id))}<br/><br/>${format(cost())} insights`;
},
canAfford() {
return player.study.insights.gte(cost());
},
buy() {
player.study.insights = player.study.insights.sub(cost());
setBuyableAmount("study", id, getBuyableAmount("study", id).add(1));
},
unlocked: () => id in player.study.cards && hasMilestone("study", 3)
};
}
function getCardAmount() {
return Object.values(player.study.cards).reduce((acc, curr) => acc + curr, 0);
}
// noinspection JSUnusedGlobalSymbols
function purchaseCard(index) {
const {card, price} = player.study.shop[index];
if (card && player.study.insights.gte(price) && getCardAmount() < 30) {
player.study.insights = player.study.insights.sub(price);
player.study.shop[index] = {card: null, price: ""};
player.study.cards[card] = (player.study.cards[card] || 0) + 1;
}
}
// noinspection JSUnusedGlobalSymbols
function toggleSelectCard(index) {
if (player.study.selected === index) {
player.study.selected = -1;
} else {
player.study.selected = index;
}
}
function getDrawDuration() {
let drawSpeed = new Decimal(10);
drawSpeed = drawSpeed.div(new Decimal(1.1).pow(getJobLevel("study")));
drawSpeed = drawSpeed.times(new Decimal(2).pow(player.study.deep));
if (player.generators.studyActive && (player.tab === "generators" || player.generators.timeLoopActive)) {
drawSpeed = drawSpeed.times(10);
}
return drawSpeed;
}
function getRefreshDraws() {
let refreshDraws = new Decimal(12);
return refreshDraws;
}
function gainStudyXp(xpGain) {
if (hasUpgrade("generators", 13)) {
xpGain = xpGain.times(layers.generators.clickables.study.effect());
}
xpGain = xpGain.times(player.study.increaseXpGain.times(new Decimal(.1).times(Decimal.pow(1.5, cardLevel("increaseXpGain")))).add(1));
xpGain = xpGain.times(ritualEffect("globalXp"));
player.study.xp = player.study.xp.add(xpGain);
checkJobXP("study");
}
function drawNextCard() {
player.study.drawProgress = 0;
let random = Math.random() * Object.values(player.study.cards).reduce((acc, curr) => acc + curr);
const ownedCards = Object.keys(player.study.cards);
let newCard = null;
for (let i = 0; i < ownedCards.length; i++) {
if (random < player.study.cards[ownedCards[i]]) {
newCard = ownedCards[i];
break;
}
random -= player.study.cards[ownedCards[i]];
}
if (newCard) {
if (player.study.lastCard && player.study.lastCard in cards && cards[player.study.lastCard].modifyNextCard) {
cards[player.study.lastCard].modifyNextCard(newCard, cardLevel(newCard));
} else if (cards[newCard].onDraw) {
cards[newCard].onDraw(cardLevel(newCard));
}
player.study.lastCard = newCard;
const card = document.getElementById("mainCard");
if (card != null) {
card.classList.remove("flipCard");
void card.offsetWidth;
card.classList.add("flipCard");
}
if (hasMilestone("study", 0)) {
player.study.refreshProgress++;
}
if (player.study.refreshProgress >= getRefreshDraws()) {
player.study.refreshProgress = 0;
player.study.shop = getShop();
for (let card of document.getElementsByClassName("shopCard")) {
if (card != null) {
card.classList.remove("flipCard");
void card.offsetWidth;
card.classList.add("flipCard");
}
}
if (player.study.notifs) {
doPopup("none", "Shop restocked", "Check it out!", 1, layers.study.color);
}
}
}
}
addLayer("study", {
name: "study",
resource: "properties studied",
image: "images/orchid_sketch.jpg",
color: studyColor,
jobName: "Study flowers",
showJobDelay: 0.25,
layerShown: () => player.chapter > 1 && hasMilestone("flowers", 4),
startData() {
return {
unlocked: true,
points: new Decimal(0),
insights: new Decimal(0),
total: new Decimal(0),
xp: new Decimal(0),
lastLevel: new Decimal(0),
timeLoopActive: false,
drawProgress: 0,
refreshProgress: 0,
cards: baseCards(),
lastCard: null,
shop: getShop(),
notifs: true,
increasePointsGain: new Decimal(0),
multiplyPointsGain: new Decimal(0),
increaseXpGain: new Decimal(0),
sellDiscount: new Decimal(0),
cardsSold: new Decimal(0),
selected: -1,
deep: new Decimal(0)
};
},
getResetGain() {
if (!tmp[this.layer].layerShown || (player.tab !== this.layer && !player[this.layer].timeLoopActive)) {
return new Decimal(0);
}
let gain = new Decimal(10);
gain = gain.times(player.study.increasePointsGain.times(new Decimal(.25).times(Decimal.pow(1.25, cardLevel("increasePointsGain")))).add(1));
gain = gain.times(new Decimal(1.02).pow(softcap(player.study.multiplyPointsGain, new Decimal(100).times(cardLevel("multiplyPointsGain").div(4).add(1)), .2)));
gain = gain.times(layers.generators.clickables[this.layer].effect());
gain = gain.times(ritualEffect("gain"));
if (player.generators.studyActive && (player.tab === "generators" || player.generators.timeLoopActive)) {
gain = gain.sqrt();
}
return gain;
},
tabFormat: {
"Main": {
content: () => {
const drawDuration = getDrawDuration();
return [
["sticky", [0, ["row", [["bar", "job"], ["display-text", `<span style="margin-left: 20px;">Lv. ${getJobLevel("study")}</span>`]]]]],
"blank",
["sticky", ["36px", ["display-text", `<span>You have <h2 style="color: ${studyColor}; text-shadow: ${studyColor} 0 0 10px">${formatWhole(player.study.points)}</h2> properties studied,<br/>and <h2 style="color: darkcyan; text-shadow: darkcyan 0 0 10px">${formatWhole(player.study.insights)}</h2> key insights</span>`]]],
"blank",
["display-text", (() => {
if (!hasMilestone("study", 0)) {
return "Discover new ways to harness the power of the cards at level 2";
}
if (!hasMilestone("study", 1)) {
return "Discover new ways to harness the power of the cards at level 4";
}
if (!hasMilestone("study", 3)) {
return "Discover new ways to harness the power of the cards at level 6";
}
if (!hasMilestone("study", 4)) {
return "Discover new ways to harness the power of the cards at level 8";
}
return "";
})()],
"blank",
["display-text", `Next card will auto-draw in ${new Decimal(drawDuration - player.study.drawProgress).clampMax(drawDuration - 0.01).toFixed(2)} seconds<br/>`],
["display-text", drawDuration / 10 > player.study.drawProgress ? `You can manually draw next card in ${new Decimal(drawDuration / 10 - player.study.drawProgress).clampMax(drawDuration / 10 - 0.01).toFixed(2)} seconds` : `You can manually draw the next card NOW`],
"blank",
["card", { card: player.study.lastCard, id: "mainCard", className: "flipCard" }],
"blank",
["clickable", "manual"],
"blank",
["milestones-filtered", [2, 5, 6]]
];
}
},
"Deck": {
content: () => [
["sticky", [0, ["row", [["bar", "job"], ["display-text", `<span style="margin-left: 20px;">Lv. ${getJobLevel("study")}</span>`]]]]],
"blank",
["clickable", "reset"],
"blank",
["row", Object.entries(player.study.cards).map(([card, amount]) => ["card", { card, note: amount }])]
]
},
"Buy Cards": {
content: () => [
["sticky", [0, ["row", [["bar", "job"], ["display-text", `<span style="margin-left: 20px;">Lv. ${getJobLevel("study")}</span>`]]]]],
"blank",
["sticky", ["36px", ["display-text", `<span>You have <h2 style="color: darkcyan; text-shadow: darkcyan 0 0 10px">${formatWhole(player.study.insights)}</h2> key insights</span>`]]],
"blank",
["row", [
["display-text", "Shop Refresh Notifications"],
"blank",
["toggle", ["study", "notifs"]]
]],
"blank",
["display-text", `Cards refresh in ${getRefreshDraws() - player.study.refreshProgress} draws`],
"blank",
["display-text", `Your deck has ${getCardAmount()} out of the 30 card limit.`],
"blank",
["row", player.study.shop.map(({card, price}, i) =>
["column", [
card == null ? ["card", { card: "soldOut" }] : ["card", { card, className: "shopCard flipCard", onclick: () => purchaseCard(i) }],
["display-text", `<div class='card-note'>${card in player.study.cards ? player.study.cards[card] : card == null ? '' : 'New!'}</div>`],
"blank",
["display-text", `<h2 style="color: darkcyan; text-shadow: darkcyan 0 0 10px">${card == null ? "" /*zero width space*/ : formatWhole(price)}</h2>`]
], {
margin: "auto 10px 20px",
cursor: "pointer",
opacity: card != null && player.study.insights.gte(price) ? 1 : 0.5
}]), {width: "100%"}]
],
unlocked: () => hasMilestone("study", 0)
},
"Destroy Cards": {
content: () => [
["sticky", [0, ["row", [["bar", "job"], ["display-text", `<span style="margin-left: 20px;">Lv. ${getJobLevel("study")}</span>`]]]]],
"blank",
["sticky", ["36px", ["display-text", `<span>You have <h2 style="color: ${studyColor}; text-shadow: ${studyColor} 0 0 10px">${formatWhole(player.study.points)}</h2> properties studied`]]],
"blank",
["sticky", ["86px", ["clickable", "sell"]]],
"blank",
["row", Object.entries(player.study.cards).map(([card, amount]) => ["card", { card, note: amount, className: player.study.selected === card ? "selectedCard cursor" : "cursor", onclick: () => toggleSelectCard(card) }]), {width: "100%"}]
],
unlocked: () => hasMilestone("study", 1)
},
"Upgrade Cards": {
content: () => [
["sticky", [0, ["row", [["bar", "job"], ["display-text", `<span style="margin-left: 20px;">Lv. ${getJobLevel("study")}</span>`]]]]],
"blank",
["sticky", ["36px", ["display-text", `<span>You have <h2 style="color: darkcyan; text-shadow: darkcyan 0 0 10px">${formatWhole(player.study.insights)}</h2> key insights`]]],
"blank",
hasMilestone("study", 4) ? ["column", [
["display-text", `Deep Thought is currently giving <span style="text-shadow: white 0 0 10px">${formatWhole(player.study.deep)}</span> bonus levels to every card,<br/>but makes each draw take <span style="text-shadow: white 0 0 10px">${formatWhole(new Decimal(2).pow(player.study.deep))}x</span> longer due to processing time.<br/><br/>You cannot add more bonus levels than your level at this job.`],
"blank",
["row", [
["clickable", "deep0"],
"blank",
["clickable", "deep-"],
"blank",
["clickable", "deep+"],
"blank",
["clickable", "deepMax"]
]],
"blank"
]] : null,
["column", Object.keys(player.study.cards).filter(card => card in layers.study.buyables).map(card => ["row", [
["card", { card }],
["display-text", "〉〉", {fontSize: "36px", margin: "10px"}],
["card", { card, overrideLevel: cardLevel(card).add(1) }],
["buyable", card]
]])]
],
unlocked: () => hasMilestone("study", 3),
shouldNotify: () => Object.values(tmp.study.buyables).some(buyable => buyable.unlocked && buyable.canAfford)
}
},
update(diff) {
if (player.tab === this.layer || player[this.layer].timeLoopActive) {
if (player.generators.studyActive && (player.tab === "generators" || player.generators.timeLoopActive)) {
diff = diff / 10;
}
player[this.layer].drawProgress += diff;
// TODO draws/sec
if (player[this.layer].drawProgress > getDrawDuration()) {
drawNextCard();
}
}
},
onAddPoints(gain) {
gainStudyXp(gain);
},
milestones: {
0: {
requirementDescription: "Level 2",
done: () => player.study.xp.gte(10)
},
1: {
requirementDescription: "Level 4",
done: () => player.study.xp.gte(1e3)
},
2: {
title: "And all dared to brave unknown terrors, to do mighty deeds,",
requirementDescription: "Level 5",
"effectDescription": "Unlock distill flowers job",
done: () => player.study.xp.gte(1e4)
},
3: {
requirementDescription: "Level 6",
done: () => player.study.xp.gte(1e5)
},
4: {
requirementDescription: "Level 8",
done: () => player.study.xp.gte(1e7)
},
5: {
title: "to boldly split infinitives that no man had split before—",
requirementDescription: "Level 10",
"effectDescription": "Unlock experiments job",
done: () => player.study.xp.gte(1e9),
unlocked: () => hasMilestone("study", 2)
},
6: {
title: "and thus was the Empire forged.",
requirementDescription: "Level 25",
"effectDescription": "Unlock ???",
done: () => player.study.xp.gte(1e24) && player.chapter > 2,
unlocked: () => hasMilestone("study", 5) && player.chapter > 2
}
},
clickables: {
manual: {
title: "Think before you pluck. Irresponsible plucking costs lives.",
display: "Draw next card",
canClick: () => getDrawDuration() / 10 < player.study.drawProgress,
onClick: drawNextCard
},
reset: {
title: "RESET DECK",
style: {
background: "#dc3545"
},
display: "Reset your deck to the basic starter deck. Resets destroyed cards and played cards counts. Does not reset the rest of this job.",
canClick: true,
onClick: () => {
if (confirm("Are you sure you want to reset your deck to the starter deck?")) {
player.study.cards = baseCards();
player.study.cardsSold = new Decimal(0);
player.study.lastCard = null;
player.study.increasePointsGain = new Decimal(0);
player.study.multiplyPointsGain = new Decimal(0);
player.study.increaseXpGain = new Decimal(0);
player.study.sellDiscount = new Decimal(0);
}
}
},
sell: {
title: "They obstinately persisted in their absence.<br/>",
style: {
width: "200px",
height: "200px"
},
display() {
return `Remove a card from your deck. Cost multiplies by 10 for each card manually destroyed.<br/><br/>Cost: ${formatWhole(this.cost())} properties studied`;
},
cost(x) {
let cost = new Decimal(500).times(new Decimal(10).pow(player[this.layer].cardsSold));
cost = cost.times(new Decimal(0.98).pow(softcap(player.study.sellDiscount, new Decimal(100).times(cardLevel("multiplyPointsGain").div(4).add(1)), .5)));
return cost;
},
canClick() {
if (!(player.study.selected in player.study.cards)) {
return false;
}
if (player.study.cards[player.study.selected] <= 0) {
return false;
}
return player[this.layer].points.gte(this.cost());
},
onClick() {
player[this.layer].points = player[this.layer].points.sub(this.cost());
player[this.layer].cardsSold = player[this.layer].cardsSold.add(1);
player[this.layer].cards[player[this.layer].selected] = (player[this.layer].cards[player[this.layer].selected] || 0) - 1;
if (player[this.layer].cards[player[this.layer].selected] <= 0) {
delete player[this.layer].cards[player[this.layer].selected];
player[this.layer].selected = -1;
}
},
unlocked: () => hasMilestone("study", 1),
layer: "study"
},
"deep0": {
title: "0",
style: {
width: "60px",
minHeight: "60px"
},
canClick: () => player.study.deep.neq(0),
onClick: () => {
player.study.deep = new Decimal(0);
}
},
"deep-": {
title: "- 1",
style: {
width: "60px",
minHeight: "60px"
},
canClick: () => player.study.deep.gt(0),
onClick: () => {
player.study.deep = player.study.deep.sub(1);
}
},
"deep+": {
title: "+ 1",
style: {
width: "60px",
minHeight: "60px"
},
canClick: () => player.study.deep.lt(getJobLevel("study")),
onClick: () => {
player.study.deep = player.study.deep.add(1);
}
},
"deepMax": {
title: () => formatWhole(getJobLevel("study")),
style: {
width: "60px",
minHeight: "60px"
},
canClick: () => player.study.deep.neq(getJobLevel("study")),
onClick: () => {
player.study.deep = getJobLevel("study");
}
}
},
buyables: {
gainPoints: getCardUpgradeBuyable("gainPoints"),
gainBigPoints: getCardUpgradeBuyable("gainBigPoints"),
gainInsight: getCardUpgradeBuyable("gainInsight"),
gainBigInsight: getCardUpgradeBuyable("gainBigInsight"),
playTwice: getCardUpgradeBuyable("playTwice"),
increasePointsGain: getCardUpgradeBuyable("increasePointsGain"),
multiplyPointsGain: getCardUpgradeBuyable("multiplyPointsGain"),
sellDiscount: getCardUpgradeBuyable("sellDiscount"),
gainXp: getCardUpgradeBuyable("gainXp"),
increaseXpGain: getCardUpgradeBuyable("increaseXpGain"),
},
bars: {
job: getJobProgressBar("study", studyColor)
}
});
// Names references:
// https://www.shmoop.com/study-guides/literature/hitchhikers-guide-to-the-galaxy/quotes
// https://en.wikiquote.org/wiki/The_Hitchhiker's_Guide_to_the_Galaxy