mirror of
https://github.com/thepaperpilot/Planar-Pioneers.git
synced 2024-11-24 17:31:47 +00:00
Implement booster machine
This commit is contained in:
parent
a61e113109
commit
03b8dece48
2 changed files with 239 additions and 9 deletions
|
@ -29,6 +29,7 @@ import { VueFeature, render, renderRow, trackHover } from "util/vue";
|
||||||
import { ComputedRef, Ref, computed, ref, unref } from "vue";
|
import { ComputedRef, Ref, computed, ref, unref } from "vue";
|
||||||
import { createCollapsibleModifierSections, createFormulaPreview, estimateTime } from "./common";
|
import { createCollapsibleModifierSections, createFormulaPreview, estimateTime } from "./common";
|
||||||
import {
|
import {
|
||||||
|
BoosterState,
|
||||||
InfluenceState,
|
InfluenceState,
|
||||||
Influences,
|
Influences,
|
||||||
influences as influenceTypes,
|
influences as influenceTypes,
|
||||||
|
@ -45,7 +46,7 @@ import TooltipVue from "features/tooltips/Tooltip.vue";
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
export type Treasure = GenericAchievement & {
|
export type Treasure = GenericAchievement & {
|
||||||
update?: (diff: number) => void;
|
update?: (diff: DecimalSource) => void;
|
||||||
link?: ComputedRef<BoardNode>;
|
link?: ComputedRef<BoardNode>;
|
||||||
effectedResource?: Resources | "energy";
|
effectedResource?: Resources | "energy";
|
||||||
resourceMulti: DecimalSource;
|
resourceMulti: DecimalSource;
|
||||||
|
@ -390,7 +391,7 @@ export function createPlane(
|
||||||
treasureType = "relic";
|
treasureType = "relic";
|
||||||
}
|
}
|
||||||
let description = "";
|
let description = "";
|
||||||
let update: (diff: number) => void;
|
let update: (diff: DecimalSource) => void;
|
||||||
let onComplete: VoidFunction;
|
let onComplete: VoidFunction;
|
||||||
let link: ComputedRef<BoardNode>;
|
let link: ComputedRef<BoardNode>;
|
||||||
let randomResource: Resources;
|
let randomResource: Resources;
|
||||||
|
@ -526,12 +527,40 @@ export function createPlane(
|
||||||
RepeatableType
|
RepeatableType
|
||||||
) as GenericRepeatable[];
|
) as GenericRepeatable[];
|
||||||
|
|
||||||
|
const planarSpeedModifier = createSequentialModifier(() => [
|
||||||
|
createMultiplicativeModifier(() => ({
|
||||||
|
multiplier: () =>
|
||||||
|
Decimal.add(
|
||||||
|
(
|
||||||
|
main.board.types.booster.nodes.value[0]?.state as unknown as
|
||||||
|
| BoosterState
|
||||||
|
| undefined
|
||||||
|
)?.level ?? 0,
|
||||||
|
1
|
||||||
|
),
|
||||||
|
description: "Booster",
|
||||||
|
enabled: () =>
|
||||||
|
(
|
||||||
|
main.board.types.booster.nodes.value[0]?.state as unknown as
|
||||||
|
| BoosterState
|
||||||
|
| undefined
|
||||||
|
)?.portals.includes(id) ?? false
|
||||||
|
}))
|
||||||
|
]);
|
||||||
|
const computedPlanarSpeedModifier = computed(() => planarSpeedModifier.apply(1));
|
||||||
|
|
||||||
const [resourceTab, resourceTabCollapsed] = createCollapsibleModifierSections(() => [
|
const [resourceTab, resourceTabCollapsed] = createCollapsibleModifierSections(() => [
|
||||||
{
|
{
|
||||||
title: `${camelToTitle(resource.displayName)} Gain`,
|
title: `${camelToTitle(resource.displayName)} Gain`,
|
||||||
modifier: resourceGainModifier,
|
modifier: resourceGainModifier,
|
||||||
base: 0,
|
base: 0,
|
||||||
unit: "/s"
|
unit: "/s"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: `${camelToTitle(resource.displayName)} Time Speed`,
|
||||||
|
modifier: planarSpeedModifier,
|
||||||
|
base: 1,
|
||||||
|
visible: () => Decimal.gt(computedPlanarSpeedModifier.value, 1)
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
const showModifiersModal = ref(false);
|
const showModifiersModal = ref(false);
|
||||||
|
@ -552,12 +581,15 @@ export function createPlane(
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const totalDiff = Decimal.times(computedPlanarSpeedModifier.value, diff);
|
||||||
|
|
||||||
timeActive.value = Decimal.add(timeActive.value, diff);
|
timeActive.value = Decimal.add(timeActive.value, totalDiff);
|
||||||
resource.value = Decimal.times(computedResourceGain.value, diff).add(resource.value);
|
resource.value = Decimal.times(computedResourceGain.value, totalDiff).add(
|
||||||
|
resource.value
|
||||||
|
);
|
||||||
|
|
||||||
earnedTreasures.value.forEach(treasure => {
|
earnedTreasures.value.forEach(treasure => {
|
||||||
treasure.update?.(diff);
|
treasure.update?.(totalDiff);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,13 @@ export interface InfluenceState {
|
||||||
data: State;
|
data: State;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BoosterState {
|
||||||
|
portals: string[];
|
||||||
|
maxConnections: number;
|
||||||
|
powered: boolean;
|
||||||
|
level: DecimalSource;
|
||||||
|
}
|
||||||
|
|
||||||
export const mineLootTable = {
|
export const mineLootTable = {
|
||||||
dirt: 120,
|
dirt: 120,
|
||||||
sand: 60,
|
sand: 60,
|
||||||
|
@ -160,7 +167,7 @@ const tools = {
|
||||||
cost: 1e15,
|
cost: 1e15,
|
||||||
name: "Booster",
|
name: "Booster",
|
||||||
type: "booster",
|
type: "booster",
|
||||||
state: { planes: [], maxConnections: 1, powered: false }
|
state: { planes: [], maxConnections: 1, powered: false, level: 1 }
|
||||||
},
|
},
|
||||||
emerald: {
|
emerald: {
|
||||||
cost: 1e19,
|
cost: 1e19,
|
||||||
|
@ -195,7 +202,8 @@ const tools = {
|
||||||
ultimatum: {
|
ultimatum: {
|
||||||
cost: 1e54,
|
cost: 1e54,
|
||||||
name: "Investments",
|
name: "Investments",
|
||||||
type: "investments"
|
type: "investments",
|
||||||
|
state: { planes: [], maxConnections: 1, powered: false }
|
||||||
}
|
}
|
||||||
} as const satisfies Record<
|
} as const satisfies Record<
|
||||||
Resources,
|
Resources,
|
||||||
|
@ -443,6 +451,8 @@ export const influences = {
|
||||||
>;
|
>;
|
||||||
export type Influences = keyof typeof influences;
|
export type Influences = keyof typeof influences;
|
||||||
|
|
||||||
|
const increaseBoostFormula = Formula.variable(0).add(8).times(2).pow10();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hidden
|
* @hidden
|
||||||
*/
|
*/
|
||||||
|
@ -466,7 +476,8 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
sand: board.types.dowsing.nodes.value[0],
|
sand: board.types.dowsing.nodes.value[0],
|
||||||
wood: board.types.quarry.nodes.value[0],
|
wood: board.types.quarry.nodes.value[0],
|
||||||
coal: board.types.empowerer.nodes.value[0],
|
coal: board.types.empowerer.nodes.value[0],
|
||||||
iron: board.types.portalGenerator.nodes.value[0]
|
iron: board.types.portalGenerator.nodes.value[0],
|
||||||
|
gold: board.types.booster.nodes.value[0]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const influenceNodes: ComputedRef<Record<Influences, BoardNode>> = computed(() => ({
|
const influenceNodes: ComputedRef<Record<Influences, BoardNode>> = computed(() => ({
|
||||||
|
@ -727,6 +738,33 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function labelForAcceptingPortal(
|
||||||
|
node: BoardNode,
|
||||||
|
description: (portal: string) => string
|
||||||
|
): NodeLabel | null {
|
||||||
|
if ((board as GenericBoard).draggingNode.value?.type === "portal") {
|
||||||
|
const portal = (
|
||||||
|
(board as GenericBoard).draggingNode.value?.state as unknown as PortalState
|
||||||
|
).id;
|
||||||
|
const { maxConnections, portals } = node.state as unknown as BoosterState;
|
||||||
|
if (portals.includes(portal)) {
|
||||||
|
return { text: "Disconnect", color: "var(--accent2)" };
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
Decimal.add(maxConnections, computedBonusConnectionsModifier.value).lte(
|
||||||
|
portals.length
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return { text: "Max connections", color: "var(--danger)" };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
text: description(portal),
|
||||||
|
color: "var(--accent2)"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function canAcceptResource(node: BoardNode, otherNode: BoardNode) {
|
function canAcceptResource(node: BoardNode, otherNode: BoardNode) {
|
||||||
if (otherNode.type !== "resource") {
|
if (otherNode.type !== "resource") {
|
||||||
return false;
|
return false;
|
||||||
|
@ -801,6 +839,43 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
board.selectedNode.value = node;
|
board.selectedNode.value = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canAcceptPortal(node: BoardNode, otherNode: BoardNode) {
|
||||||
|
if (otherNode.type !== "portal") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const portal = (otherNode.state as unknown as PortalState).id;
|
||||||
|
const { maxConnections, portals } = node.state as unknown as BoosterState;
|
||||||
|
if (portals.includes(portal)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
Decimal.add(maxConnections, computedBonusConnectionsModifier.value).lte(portals.length)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDropPortal(node: BoardNode, otherNode: BoardNode) {
|
||||||
|
if (otherNode.type !== "portal") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const portal = (otherNode.state as unknown as PortalState).id;
|
||||||
|
const { portals } = node.state as unknown as BoosterState;
|
||||||
|
if (portals.includes(portal)) {
|
||||||
|
node.state = {
|
||||||
|
...(node.state as object),
|
||||||
|
tools: portals.filter(r => r !== portal)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
node.state = {
|
||||||
|
...(node.state as object),
|
||||||
|
tools: [...portals, portal]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
board.selectedNode.value = node;
|
||||||
|
}
|
||||||
|
|
||||||
const board = createBoard(board => ({
|
const board = createBoard(board => ({
|
||||||
startNodes: () => [
|
startNodes: () => [
|
||||||
{ position: { x: 0, y: 0 }, type: "mine", state: { progress: 0, powered: false } },
|
{ position: { x: 0, y: 0 }, type: "mine", state: { progress: 0, powered: false } },
|
||||||
|
@ -1368,6 +1443,95 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
},
|
},
|
||||||
outlineColor: "var(--danger)",
|
outlineColor: "var(--danger)",
|
||||||
draggable: true
|
draggable: true
|
||||||
|
},
|
||||||
|
booster: {
|
||||||
|
shape: Shape.Diamond,
|
||||||
|
size: 50,
|
||||||
|
title: "⌛",
|
||||||
|
label: node => {
|
||||||
|
if (node === board.selectedNode.value) {
|
||||||
|
return {
|
||||||
|
text:
|
||||||
|
(node.state as unknown as BoosterState).portals.length === 0
|
||||||
|
? "Booster - Drag a portal to me!"
|
||||||
|
: `Boosting by ${formatWhole(
|
||||||
|
Decimal.add(
|
||||||
|
1,
|
||||||
|
(node.state as unknown as BoosterState).level
|
||||||
|
)
|
||||||
|
)}x (${
|
||||||
|
(node.state as { tools: Passives[] }).tools.length
|
||||||
|
}/${Decimal.add(
|
||||||
|
(node.state as { maxConnections: number }).maxConnections,
|
||||||
|
computedBonusConnectionsModifier.value
|
||||||
|
)})`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return labelForAcceptingPortal(node, portal => {
|
||||||
|
return `Boost ${(layers[portal] as GenericPlane).name}'s speed`;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
actionDistance: Math.PI / 4,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
id: "deselect",
|
||||||
|
icon: "close",
|
||||||
|
tooltip: {
|
||||||
|
text: "Disconnect portals"
|
||||||
|
},
|
||||||
|
onClick(node: BoardNode) {
|
||||||
|
node.state = { ...(node.state as object), portals: [] };
|
||||||
|
board.selectedAction.value = null;
|
||||||
|
board.selectedNode.value = null;
|
||||||
|
},
|
||||||
|
visibility: (node: BoardNode) =>
|
||||||
|
(node.state as unknown as BoosterState)?.portals.length ?? 0 > 0
|
||||||
|
},
|
||||||
|
getIncreaseConnectionsAction(x => x.add(6).pow_base(1000)),
|
||||||
|
{
|
||||||
|
id: "increaseBoost",
|
||||||
|
icon: "arrow_upward",
|
||||||
|
tooltip(node: BoardNode) {
|
||||||
|
return {
|
||||||
|
text: `Increase boost - ${formatWhole(
|
||||||
|
increaseBoostFormula.evaluate(
|
||||||
|
(node.state as unknown as BoosterState).level
|
||||||
|
)
|
||||||
|
)} energy`
|
||||||
|
};
|
||||||
|
},
|
||||||
|
confirmationLabel(node: BoardNode) {
|
||||||
|
return Decimal.gte(
|
||||||
|
energy.value,
|
||||||
|
increaseBoostFormula.evaluate(
|
||||||
|
(node.state as unknown as BoosterState).level
|
||||||
|
)
|
||||||
|
)
|
||||||
|
? { text: "Tap again to confirm" }
|
||||||
|
: { text: "Cannot afford", color: "var(--danger)" };
|
||||||
|
},
|
||||||
|
onClick(node: BoardNode) {
|
||||||
|
const cost = increaseBoostFormula.evaluate(
|
||||||
|
(node.state as unknown as BoosterState).level
|
||||||
|
);
|
||||||
|
if (Decimal.gte(energy.value, cost)) {
|
||||||
|
energy.value = Decimal.sub(energy.value, cost);
|
||||||
|
}
|
||||||
|
node.state = {
|
||||||
|
...(node.state as object),
|
||||||
|
level: Decimal.add((node.state as unknown as BoosterState).level, 1)
|
||||||
|
};
|
||||||
|
board.selectedAction.value = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
togglePoweredAction
|
||||||
|
],
|
||||||
|
canAccept: canAcceptPortal,
|
||||||
|
onDrop: onDropPortal,
|
||||||
|
classes: node => ({
|
||||||
|
running: isPowered(node)
|
||||||
|
}),
|
||||||
|
draggable: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
|
@ -1491,6 +1655,21 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
return links;
|
return links;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (booster.value != null) {
|
||||||
|
(booster.value.state as unknown as BoosterState).portals.forEach(portal => {
|
||||||
|
links.push({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
startNode: booster.value!,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
endNode: (board as GenericBoard).types.portal.nodes.value.find(
|
||||||
|
node => (node.state as unknown as PortalState).id === portal
|
||||||
|
)!,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
stroke: isPowered(booster.value!) ? "var(--accent1)" : "var(--foreground)",
|
||||||
|
strokeWidth: 4
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Object.values(influenceNodes.value).forEach(node => {
|
Object.values(influenceNodes.value).forEach(node => {
|
||||||
const state = node.state as unknown as InfluenceState;
|
const state = node.state as unknown as InfluenceState;
|
||||||
if (state.type === "increaseResources" || state.type === "decreaseResources") {
|
if (state.type === "increaseResources" || state.type === "decreaseResources") {
|
||||||
|
@ -1522,7 +1701,8 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
const portalGenerator: ComputedRef<BoardNode | undefined> = computed(
|
const portalGenerator: ComputedRef<BoardNode | undefined> = computed(
|
||||||
() => toolNodes.value.iron
|
() => toolNodes.value.iron
|
||||||
);
|
);
|
||||||
const poweredMachines = [mine, dowsing, quarry, empowerer];
|
const booster: ComputedRef<BoardNode | undefined> = computed(() => toolNodes.value.gold);
|
||||||
|
const poweredMachines = [mine, dowsing, quarry, empowerer, booster];
|
||||||
|
|
||||||
function grantResource(type: Resources, amount: DecimalSource) {
|
function grantResource(type: Resources, amount: DecimalSource) {
|
||||||
let node = resourceNodes.value[type];
|
let node = resourceNodes.value[type];
|
||||||
|
@ -1981,6 +2161,24 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (booster.value) {
|
||||||
|
const maxConnections = (booster.value.state as unknown as BoosterState)
|
||||||
|
.maxConnections;
|
||||||
|
if (
|
||||||
|
Decimal.lt(
|
||||||
|
(booster.value.state as unknown as BoosterState).portals.length,
|
||||||
|
Decimal.add(maxConnections, curr)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
booster.value.state = {
|
||||||
|
...(booster.value.state as object),
|
||||||
|
resources: (booster.value.state as unknown as BoosterState).portals.slice(
|
||||||
|
0,
|
||||||
|
Decimal.add(maxConnections, curr).toNumber()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue