pages/kronos/js/Layers/rituals.js

360 lines
12 KiB
JavaScript
Raw Normal View History

Vue.component("rune", {
props: ["layer", "data"],
template: `<div class="upgAlign">
<button class="upg can rune" style="width: 60px; min-height: 60px;"
v-on:click="setRune(data)"
v-bind:style="{
background: (player.rituals.board[\`\$\{data[0]\}\$\{data[1]\}\`] ? 'url(images/' + player.rituals.board[\`\$\{data[0]\}\$\{data[1]\}\`] + 'Rune.webp) no-repeat center / contain ' : '') + (layers[player.rituals.board[\`\$\{data[0]\}\$\{data[1]\}\`]]?.color || ritualsColor)
}">
</button>
</div>`
});
Vue.component("ritual", {
props: ["layer", "data"],
template: `<div class="ritual">
<span>
<div v-for="(row, index) in data.pattern" v-key="index" style="text-align: center;">
<div v-for="(symbol, index) in row" v-key="index" class="upgAlign" style="display: inline-block;">
<span class="upg can rune"
style="display: inline-block; width: 60px; min-height: 60px; font-size: xx-large; text-align: center; line-height: 60px;"
v-bind:style="{ backgroundColor: symbol == null ? '#3a3e45' : ritualsColor }">{{ symbol == null ? '' : '#' + symbol }}</span>
</div>
</div>
</span>
<span><h3>{{ data.title }}</h3>{{ data.description }}</span>
</div>`
});
const rituals = {
xp: {
title: "Ritual of Doctrina",
description: "Each of these rituals exponentially increases the amount of ritual xp gained per second",
pattern: [ [ 0, 1 ], [ 1, 0 ] ],
effect: (amount, effectiveness) => Decimal.pow(4, amount).sub(1).times(effectiveness),
effectDisplay: () => format(ritualEffect("xp")) + " xp/s",
unlocked: () => true
},
gain: {
title: "Ritual of Emolumentum",
description: "Each of these rituals increases the amount of each job's primary resource is gained",
pattern: [ [ 0, 1, 1, 0 ] ],
effect: (amount, effectiveness) => new Decimal(amount).times(effectiveness).add(1),
effectDisplay: () => "x" + format(ritualEffect("gain")) + " all job's primary resources",
unlocked: () => hasMilestone("rituals", 0)
},
improvement: {
title: "Ritual of Melius",
description: "Each of these rituals increases the effectiveness of all other rituals over time, with diminishing returns",
pattern: [ [ 0, 1, 0 ], [ 1, 2, 1 ], [ 0, 1, 0 ] ],
effect: amount => new Decimal(amount).pow(2).times(.01),
effectDisplay: () => "+" + format(ritualEffect("improvement")) + " increased effectiveness of all other ritual effects/s",
unlocked: () => hasMilestone("rituals", 1)
},
globalXp: {
title: "Ritual of Colegium",
description: "Each of these rituals increases the amount of each job's xp gain",
pattern: [ [ 0, null, 0 ], [ null, null, null ], [ 0, null, 0 ] ],
effect: (amount, effectiveness) => new Decimal(amount).times(effectiveness).add(1),
effectDisplay: () => "x" + format(ritualEffect("globalXp")) + " all job's xp gain",
unlocked: () => hasMilestone("rituals", 3)
},
speed: {
title: "Ritual of Celeritas",
description: "Each of these rituals increases the flow of time itself",
pattern: [ [ 0, null, null, 0 ], [ null, 1, 1, null ], [ null, 1, 1, null ], [ 0, null, null, 0 ] ],
effect: (amount, effectiveness) => new Decimal(amount).times(effectiveness).add(1),
effectDisplay: () => "x" + format(ritualEffect("speed")) + " global speed multiplier",
unlocked: () => hasMilestone("rituals", 3)
}
};
function ritualEffect(id) {
let level = player.tab === "rituals" || player.rituals.timeLoopActive ? player.rituals.rituals[id] || 0 : 0;
let effect = rituals[id].effect(level, player.rituals.effectiveness.max(1).times(Decimal.pow(1.1, getJobLevel("rituals"))).log2().add(1));
if (player.generators.ritualsActive && (player.tab === "generators" || player.generators.timeLoopActive)) {
effect = effect.sqrt();
}
return effect;
}
// Note: id is the corresponding *buyable* ID
function createRuneSelector(id, rune) {
// TODO image based on rune
return {
color: layers[rune]?.color || ritualsColor,
class: {
rune: true
},
style: {
width: "60px",
minHeight: "60px",
background: rune ? 'url(images/' + rune + 'Rune.webp) no-repeat center / contain' : '',
backgroundColor: layers[rune]?.color || ritualsColor,
"--count": rune === null ? "" : () => (getBuyableAmount("rituals", id)?.toNumber() || 0) - Object.values(player.rituals.board).filter(r => r === rune).length
},
canClick: () => player.rituals.selectedRune !== rune,
onClick: () => player.rituals.selectedRune = rune
}
}
function createRuneBuyable(id, title) {
return {
title: title + "<br/>",
display() {
return `Craft another rune<br/><br/>Currently: ${formatWhole(getBuyableAmount("rituals", this.id))}<br/><br/>Cost: ${format(this.cost())} ${layers[id].resource}`;
},
runeType: id,
color: layers[id].color,
style: {
width: '160px',
height: '160px'
},
cost(x) {
const amount = x || getBuyableAmount("rituals", this.id);
return new Decimal(1e9).times(new Decimal(10).pow(amount));
},
canAfford() {
return player[id].points.gte(this.cost());
},
buy() {
player[id].points = player[id].points.sub(this.cost());
setBuyableAmount("rituals", this.id, getBuyableAmount("rituals", this.id).add(1));
},
unlocked: () => tmp[id].layerShown
};
}
function getRows() {
let rows = 3;
if (hasMilestone("rituals", 1)) {
rows++;
}
if (hasMilestone("rituals", 4)) {
rows++;
}
return rows;
}
function getCols() {
let cols = 3;
if (hasMilestone("rituals", 0)) {
cols++;
}
if (hasMilestone("rituals", 3)) {
cols++;
}
return cols;
}
function setRune([row, col]) {
if (player.rituals.selectedRune == null || (getBuyableAmount("rituals", Object.values(layers.rituals.buyables).find(b => b.runeType === player.rituals.selectedRune).id)?.toNumber() || 0) - Object.values(player.rituals.board).filter(r => r === player.rituals.selectedRune).length > 0) {
player.rituals.board[`${row}${col}`] = player.rituals.selectedRune;
player.rituals.rituals = getRituals();
}
}
function checkRitual(ritual, top, left) {
// Store a lookup table of what runes this pattern is using
const types = {};
for (let r = 0; r < ritual.pattern.length; r++) {
for (let c = 0; c < ritual.pattern[r].length; c++) {
let patternTile = ritual.pattern[r][c];
if (patternTile == null) {
continue;
}
let tile = player.rituals.board[`${top + r}${left + c}`];
if ((patternTile in types && types[patternTile] !== tile) ||
(!(patternTile in types) && Object.values(types).includes(tile)) ||
tile == null) {
return false;
}
types[patternTile] = tile;
}
}
return true;
}
function getRituals() {
const rows = getRows();
const cols = getCols();
const ritualCounts = {};
Object.entries(rituals).forEach(([id, ritual]) => {
if (!ritual.unlocked()) {
return;
}
let ritualCount = 0;
for (let row = 0; row < rows && row <= rows - ritual.pattern.length; row++) {
for (let col = 0; col < cols && col <= cols - ritual.pattern[0].length; col++) {
// [row, col] is the top left of the ritual
// TODO allow negative numbers to represent "not this type"
if (checkRitual(ritual, row, col)) {
ritualCount++;
}
}
}
if (ritualCount > 0) {
ritualCounts[id] = ritualCount;
}
});
return ritualCounts;
}
addLayer("rituals", {
name: "rituals",
image: "images/bright-72804.jpg",
color: ritualsColor,
jobName: "Perform Rituals",
showJobDelay: 1.25,
layerShown: () => hasMilestone("sands", 5),
tooltip: "",
startData() {
return {
unlocked: true,
xp: new Decimal(0),
lastLevel: new Decimal(0),
timeLoopActive: false,
board: {},
selectedRune: null,
rituals: {},
effectiveness: new Decimal(1)
};
},
tabFormat: {
"Main": {
content: () => [
["sticky", [0, ["row", [["bar", "job"], ["display-text", `<span style="margin-left: 20px;">Lv. ${getJobLevel("rituals")}</span>`]]]]],
"blank",
["display-text", (() => {
if (!hasMilestone("rituals", 0)) {
return "Discover new ways to harness the arcane power at level 2";
}
if (!hasMilestone("rituals", 1)) {
return "Discover new ways to harness the arcane power at level 4";
}
if (!hasMilestone("rituals", 3)) {
return "Discover new ways to harness the arcane power at level 6";
}
if (!hasMilestone("rituals", 4)) {
return "Discover new ways to harness the arcane power at level 8";
}
if (!hasMilestone("rituals", 5)) {
return "Discover new ways to harness the arcane power at level 10";
}
return "";
})()],
"blank",
"buyables",
"blank",
["sticky", ["36px", ["clickables"]]],
"blank",
...new Array(getRows()).fill(0).map((_,row) => ["row", new Array(getCols()).fill(0).map((_,col) => ["rune", [row, col]])]),
"blank",
...Object.keys(rituals).filter(id => id in player.rituals.rituals && player.rituals.rituals[id] > 0).map(id => ["display-text", `${rituals[id].title} (${player.rituals.rituals[id]}): ${rituals[id].effectDisplay()}<br/>`]),
"blank",
["milestones-filtered", [2, 5, 6]]
],
shouldNotify: () => Object.values(tmp.rituals.buyables).some(buyable => buyable.unlocked && buyable.canAfford)
},
"Ritual Book": {
content: () => [
["sticky", [0, ["row", [["bar", "job"], ["display-text", `<span style="margin-left: 20px;">Lv. ${getJobLevel("rituals")}</span>`]]]]],
"blank",
["display-text", "Form rituals in the grid to gain powerful effects. You can have multiples of each ritual, and runes can be a part of multiple, overlapping rituals.<br/><br/>For any ritual, replace the tiles with any rune, but each tile with the same number must have the same rune.<br/>Blank tiles can have anything in them."],
"blank",
"blank",
...Object.values(rituals).filter(ritual => ritual.unlocked()).map(ritual => ["ritual", ritual])
]
}
},
update(diff) {
if (player.tab === this.layer || player[this.layer].timeLoopActive) {
if (player.generators.ritualsActive && (player.tab === "generators" || player.generators.timeLoopActive)) {
diff = diff / 10;
}
player.rituals.effectiveness = player.rituals.effectiveness.add(ritualEffect("improvement").times(diff));
let xpGain = ritualEffect("xp").times(diff);
xpGain = xpGain.times(ritualEffect("globalXp"));
player[this.layer].xp = player[this.layer].xp.add(xpGain);
checkJobXP(this.layer);
}
},
milestones: {
0: {
requirementDescription: "Level 2",
done: () => player.rituals.xp.gte(10)
},
1: {
requirementDescription: "Level 4",
done: () => player.rituals.xp.gte(1e3)
},
2: {
title: "You know the laws, Miss Granger.",
requirementDescription: "Level 5",
"effectDescription": "Unlock a new feature in distill job",
done: () => player.rituals.xp.gte(1e4)
},
3: {
requirementDescription: "Level 6",
done: () => player.rituals.xp.gte(1e5)
},
4: {
requirementDescription: "Level 8",
done: () => player.rituals.xp.gte(1e7)
},
5: {
title: "You must not be seen.",
requirementDescription: "Level 10",
"effectDescription": "Unlock the Ritual of Ascensio",
done: () => player.rituals.xp.gte(1e9),
unlocked: () => hasMilestone("rituals", 2)
},
6: {
title: "And you would do well, I feel, to return before this last chime.",
requirementDescription: "Level 25",
"effectDescription": "Unlock ???",
done: () => player.rituals.xp.gte(1e24) && player.chapter > 2,
unlocked: () => hasMilestone("rituals", 5) && player.chapter > 2
}
},
clickables: {
rows: 1,
cols: 7,
11: {
title: "Clear All",
style: {
color: "white",
minHeight: "60px"
},
canClick: () => Object.keys(player.rituals.board).length > 0,
onClick: () => {
player.rituals.board = {};
player.rituals.rituals = getRituals();
}
},
12: createRuneSelector(null, null),
13: createRuneSelector(11, "flowers"),
14: createRuneSelector(12, "study"),
15: createRuneSelector(13, "distill"),
16: createRuneSelector(14, "sands"),
17: createRuneSelector(15, "generators")
},
buyables: {
rows: 1,
cols: 5,
11: createRuneBuyable("flowers", "I did my waiting!"),
12: createRuneBuyable("study", "I solemnly swear that I am up to no good."),
13: createRuneBuyable("distill", "We enter a world that is entirely our own"),
14: createRuneBuyable("sands", "Finally the flesh reflects the madness within."),
15: createRuneBuyable("generators", "Mysterious thing, time")
},
bars: {
job: getJobProgressBar("rituals", ritualsColor)
}
});
// Names from https://en.wikiquote.org/wiki/Harry_Potter_and_the_Prisoner_of_Azkaban_(film)