mirror of
https://github.com/thepaperpilot/Planar-Pioneers.git
synced 2024-11-24 09:21:45 +00:00
Project cleanup
This commit is contained in:
parent
003d304668
commit
204b6788b0
5 changed files with 1533 additions and 1474 deletions
360
src/data/boardUtils.tsx
Normal file
360
src/data/boardUtils.tsx
Normal file
|
@ -0,0 +1,360 @@
|
||||||
|
import { BoardNode, GenericBoard, GenericBoardNodeAction, NodeLabel } from "features/boards/board";
|
||||||
|
import Formula, { calculateCost } from "game/formulas/formulas";
|
||||||
|
import { GenericFormula, InvertibleIntegralFormula } from "game/formulas/types";
|
||||||
|
import Decimal, { formatWhole } from "util/bignum";
|
||||||
|
import {
|
||||||
|
BoosterState,
|
||||||
|
DowsingState,
|
||||||
|
EmpowererState,
|
||||||
|
InfluenceState,
|
||||||
|
Passives,
|
||||||
|
PortalState,
|
||||||
|
ResourceState,
|
||||||
|
Resources
|
||||||
|
} from "./data";
|
||||||
|
import { main } from "./projEntry";
|
||||||
|
import { DecimalSource } from "lib/break_eternity";
|
||||||
|
import { ComputedRef } from "vue";
|
||||||
|
|
||||||
|
export const resourceLevelFormula = Formula.variable(0).add(1);
|
||||||
|
|
||||||
|
export const deselectAllAction = {
|
||||||
|
id: "deselect",
|
||||||
|
icon: "close",
|
||||||
|
tooltip: (node: BoardNode) => ({
|
||||||
|
text: "tools" in (node.state as object) ? "Disconnect tools" : "Disconnect resources"
|
||||||
|
}),
|
||||||
|
onClick(node: BoardNode) {
|
||||||
|
if (Array.isArray((node.state as unknown as InfluenceState)?.data)) {
|
||||||
|
node.state = { ...(node.state as object), data: [] };
|
||||||
|
} else if ("resources" in (node.state as object)) {
|
||||||
|
node.state = { ...(node.state as object), resources: [] };
|
||||||
|
} else if ("tools" in (node.state as object)) {
|
||||||
|
node.state = { ...(node.state as object), tools: [] };
|
||||||
|
}
|
||||||
|
main.board.selectedAction.value = null;
|
||||||
|
main.board.selectedNode.value = null;
|
||||||
|
},
|
||||||
|
visibility: (node: BoardNode) => {
|
||||||
|
if (Array.isArray((node.state as unknown as InfluenceState)?.data)) {
|
||||||
|
return ((node.state as unknown as InfluenceState).data as string[]).length > 0;
|
||||||
|
}
|
||||||
|
if ("resources" in (node.state as object)) {
|
||||||
|
return (node.state as { resources: Resources[] }).resources.length > 0;
|
||||||
|
}
|
||||||
|
if ("tools" in (node.state as object)) {
|
||||||
|
return (node.state as { tools: Passives[] }).tools.length > 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const togglePoweredAction = {
|
||||||
|
id: "toggle",
|
||||||
|
icon: "bolt",
|
||||||
|
tooltip: (node: BoardNode): NodeLabel => ({
|
||||||
|
text: (node.state as { powered: boolean }).powered
|
||||||
|
? "Turn Off"
|
||||||
|
: `Turn On - Always runs for ${formatWhole(main.nextPowerCost.value)} energy/s`
|
||||||
|
}),
|
||||||
|
onClick(node: BoardNode) {
|
||||||
|
node.state = {
|
||||||
|
...(node.state as object),
|
||||||
|
powered: !(node.state as { powered: boolean }).powered
|
||||||
|
};
|
||||||
|
main.board.selectedAction.value = null;
|
||||||
|
},
|
||||||
|
fillColor: (node: BoardNode) =>
|
||||||
|
(node.state as { powered: boolean }).powered ? "var(--accent1)" : "var(--locked)"
|
||||||
|
} as GenericBoardNodeAction;
|
||||||
|
|
||||||
|
export function getIncreaseConnectionsAction(
|
||||||
|
cost: (x: InvertibleIntegralFormula) => GenericFormula,
|
||||||
|
maxConnections = Infinity
|
||||||
|
) {
|
||||||
|
const formula = cost(Formula.variable(0));
|
||||||
|
return {
|
||||||
|
id: "moreConnections",
|
||||||
|
icon: "hub",
|
||||||
|
formula,
|
||||||
|
tooltip(node: BoardNode) {
|
||||||
|
return {
|
||||||
|
text: `Increase Connections - ${formatWhole(
|
||||||
|
formula.evaluate((node.state as { maxConnections: number }).maxConnections)
|
||||||
|
)} energy`
|
||||||
|
};
|
||||||
|
},
|
||||||
|
confirmationLabel: (node: BoardNode): NodeLabel =>
|
||||||
|
Decimal.gte(
|
||||||
|
main.energy.value,
|
||||||
|
formula.evaluate((node.state as { maxConnections: number }).maxConnections)
|
||||||
|
)
|
||||||
|
? { text: "Tap again to confirm" }
|
||||||
|
: { text: "Cannot afford", color: "var(--danger)" },
|
||||||
|
onClick(node: BoardNode) {
|
||||||
|
const cost = formula.evaluate(
|
||||||
|
(node.state as { maxConnections: number }).maxConnections
|
||||||
|
);
|
||||||
|
if (Decimal.gte(main.energy.value, cost)) {
|
||||||
|
main.energy.value = Decimal.sub(main.energy.value, cost);
|
||||||
|
}
|
||||||
|
node.state = {
|
||||||
|
...(node.state as object),
|
||||||
|
maxConnections: Decimal.add(
|
||||||
|
(node.state as { maxConnections: number }).maxConnections,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
};
|
||||||
|
main.board.selectedAction.value = null;
|
||||||
|
},
|
||||||
|
visibility: (node: BoardNode): boolean =>
|
||||||
|
Decimal.add(
|
||||||
|
(node.state as { maxConnections: number }).maxConnections,
|
||||||
|
main.computedBonusConnectionsModifier.value
|
||||||
|
).lt(maxConnections)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function labelForAcceptingResource(
|
||||||
|
node: BoardNode,
|
||||||
|
description: (resource: Resources) => string
|
||||||
|
): NodeLabel | null {
|
||||||
|
if ((main.board as GenericBoard).draggingNode.value?.type === "resource") {
|
||||||
|
const resource = (
|
||||||
|
(main.board as GenericBoard).draggingNode.value?.state as unknown as ResourceState
|
||||||
|
).type;
|
||||||
|
const { maxConnections, resources } = node.state as unknown as DowsingState;
|
||||||
|
if (resources.includes(resource)) {
|
||||||
|
return { text: "Disconnect", color: "var(--accent2)" };
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
Decimal.add(maxConnections, main.computedBonusConnectionsModifier.value).lte(
|
||||||
|
resources.length
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return { text: "Max connections", color: "var(--danger)" };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
text: description(resource),
|
||||||
|
color: "var(--accent2)"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function labelForAcceptingTool(
|
||||||
|
node: BoardNode,
|
||||||
|
description: (passive: Passives) => string
|
||||||
|
): NodeLabel | null {
|
||||||
|
if ((main.board as GenericBoard).draggingNode.value?.type === "passive") {
|
||||||
|
const passive = (main.board as GenericBoard).draggingNode.value?.state as Passives;
|
||||||
|
const { maxConnections, tools } = node.state as unknown as EmpowererState;
|
||||||
|
if (tools.includes(passive)) {
|
||||||
|
return { text: "Disconnect", color: "var(--accent2)" };
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
Decimal.add(maxConnections, main.computedBonusConnectionsModifier.value).lte(
|
||||||
|
tools.length
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return { text: "Max connections", color: "var(--danger)" };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
text: description(passive),
|
||||||
|
color: "var(--accent2)"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function labelForAcceptingPortal(
|
||||||
|
node: BoardNode,
|
||||||
|
description: (portal: string) => string
|
||||||
|
): NodeLabel | null {
|
||||||
|
if ((main.board as GenericBoard).draggingNode.value?.type === "portal") {
|
||||||
|
const portal = (
|
||||||
|
(main.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, main.computedBonusConnectionsModifier.value).lte(
|
||||||
|
portals.length
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return { text: "Max connections", color: "var(--danger)" };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
text: description(portal),
|
||||||
|
color: "var(--accent2)"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canAcceptResource(node: BoardNode, otherNode: BoardNode) {
|
||||||
|
if (otherNode.type !== "resource") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const resource = (otherNode.state as unknown as ResourceState).type;
|
||||||
|
const { maxConnections, resources } = node.state as unknown as DowsingState;
|
||||||
|
if (resources.includes(resource)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
Decimal.add(maxConnections, main.computedBonusConnectionsModifier.value).lte(
|
||||||
|
resources.length
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onDropResource(node: BoardNode, otherNode: BoardNode) {
|
||||||
|
if (otherNode.type !== "resource") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const resource = (otherNode.state as unknown as ResourceState).type;
|
||||||
|
const resources = (node.state as unknown as { resources: Resources[] }).resources;
|
||||||
|
if (resources.includes(resource)) {
|
||||||
|
node.state = {
|
||||||
|
...(node.state as object),
|
||||||
|
resources: resources.filter(r => r !== resource)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
node.state = {
|
||||||
|
...(node.state as object),
|
||||||
|
resources: [...resources, resource]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
main.board.selectedNode.value = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canAcceptTool(node: BoardNode, otherNode: BoardNode) {
|
||||||
|
if (otherNode.type !== "passive") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const passive = otherNode.state as Passives;
|
||||||
|
const { maxConnections, tools } = node.state as unknown as EmpowererState;
|
||||||
|
if (tools.includes(passive)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
Decimal.add(maxConnections, main.computedBonusConnectionsModifier.value).lte(tools.length)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onDropTool(node: BoardNode, otherNode: BoardNode) {
|
||||||
|
if (otherNode.type !== "passive") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const passive = otherNode.state as Passives;
|
||||||
|
const tools = (node.state as unknown as { tools: Passives[] }).tools;
|
||||||
|
if (tools.includes(passive)) {
|
||||||
|
node.state = {
|
||||||
|
...(node.state as object),
|
||||||
|
tools: tools.filter(r => r !== passive)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
node.state = {
|
||||||
|
...(node.state as object),
|
||||||
|
tools: [...tools, passive]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
main.board.selectedNode.value = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
export 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, main.computedBonusConnectionsModifier.value).lte(portals.length)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export 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]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
main.board.selectedNode.value = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPowered(node: BoardNode): boolean {
|
||||||
|
return node === main.board.selectedNode.value || (node.state as { powered: boolean }).powered;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isEmpowered(passive: Passives): boolean {
|
||||||
|
return (
|
||||||
|
main.empowerer.value != null &&
|
||||||
|
isPowered(main.empowerer.value) &&
|
||||||
|
(main.empowerer.value.state as unknown as EmpowererState).tools.includes(passive)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getResourceLevelProgress(resource: Resources): number {
|
||||||
|
const amount =
|
||||||
|
(main.resourceNodes.value[resource]?.state as unknown as ResourceState | undefined)
|
||||||
|
?.amount ?? 0;
|
||||||
|
const currentLevel = main.resourceLevels.value[resource];
|
||||||
|
const requiredForCurrentLevel = calculateCost(resourceLevelFormula, currentLevel, true);
|
||||||
|
const requiredForNextLevel = calculateCost(
|
||||||
|
resourceLevelFormula,
|
||||||
|
Decimal.add(currentLevel, 1),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
return Decimal.sub(amount, requiredForCurrentLevel)
|
||||||
|
.max(0)
|
||||||
|
.div(Decimal.sub(requiredForNextLevel, requiredForCurrentLevel))
|
||||||
|
.toNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkConnections<T extends string>(
|
||||||
|
bonusConnections: DecimalSource,
|
||||||
|
node: ComputedRef<BoardNode | undefined>,
|
||||||
|
connectionsName: T
|
||||||
|
) {
|
||||||
|
if (node.value) {
|
||||||
|
const state = node.value.state as unknown as { [K in T]: string[] } & {
|
||||||
|
maxConnections: DecimalSource;
|
||||||
|
};
|
||||||
|
const currentConnections = state[connectionsName];
|
||||||
|
const maxConnections = state.maxConnections;
|
||||||
|
if (Decimal.lt(currentConnections.length, Decimal.add(maxConnections, bonusConnections))) {
|
||||||
|
node.value.state = {
|
||||||
|
...(node.value.state as object),
|
||||||
|
[connectionsName]: currentConnections.slice(
|
||||||
|
0,
|
||||||
|
Decimal.add(maxConnections, bonusConnections).toNumber()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
412
src/data/data.tsx
Normal file
412
src/data/data.tsx
Normal file
|
@ -0,0 +1,412 @@
|
||||||
|
import Formula from "game/formulas/formulas";
|
||||||
|
import { State } from "game/persistence";
|
||||||
|
import { DecimalSource } from "util/bignum";
|
||||||
|
|
||||||
|
export interface MineState {
|
||||||
|
progress: DecimalSource;
|
||||||
|
powered: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResourceState {
|
||||||
|
type: Resources;
|
||||||
|
amount: DecimalSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DowsingState {
|
||||||
|
resources: Resources[];
|
||||||
|
maxConnections: number;
|
||||||
|
powered: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QuarryState extends DowsingState {
|
||||||
|
progress: DecimalSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EmpowererState {
|
||||||
|
tools: Passives[];
|
||||||
|
maxConnections: number;
|
||||||
|
powered: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PortalGeneratorState {
|
||||||
|
tier: Resources | undefined;
|
||||||
|
influences: Influences[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PortalState {
|
||||||
|
id: string;
|
||||||
|
powered: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InfluenceState {
|
||||||
|
type: Influences;
|
||||||
|
data: State;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BoosterState {
|
||||||
|
portals: string[];
|
||||||
|
maxConnections: number;
|
||||||
|
powered: boolean;
|
||||||
|
level: DecimalSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mineLootTable = {
|
||||||
|
dirt: 120,
|
||||||
|
sand: 60,
|
||||||
|
gravel: 40,
|
||||||
|
wood: 30,
|
||||||
|
stone: 24,
|
||||||
|
coal: 20,
|
||||||
|
copper: 15,
|
||||||
|
iron: 12,
|
||||||
|
silver: 10,
|
||||||
|
gold: 8,
|
||||||
|
emerald: 6,
|
||||||
|
platinum: 5,
|
||||||
|
diamond: 4,
|
||||||
|
berylium: 3,
|
||||||
|
unobtainium: 2,
|
||||||
|
ultimatum: 1
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type Resources = keyof typeof mineLootTable;
|
||||||
|
export const resourceNames = Object.keys(mineLootTable) as Resources[];
|
||||||
|
|
||||||
|
export const tools = {
|
||||||
|
dirt: {
|
||||||
|
cost: 1000,
|
||||||
|
name: "Pickaxe",
|
||||||
|
type: "passive",
|
||||||
|
state: "dirt"
|
||||||
|
},
|
||||||
|
sand: {
|
||||||
|
cost: 1e4,
|
||||||
|
name: "Dowsing Rod",
|
||||||
|
type: "dowsing",
|
||||||
|
state: { resources: [], maxConnections: 1, powered: false }
|
||||||
|
},
|
||||||
|
gravel: {
|
||||||
|
cost: 1e5,
|
||||||
|
name: "Ore Processor",
|
||||||
|
type: "passive",
|
||||||
|
state: "gravel"
|
||||||
|
},
|
||||||
|
wood: {
|
||||||
|
cost: 1e6,
|
||||||
|
name: "Quarry",
|
||||||
|
type: "quarry",
|
||||||
|
state: { resources: [], maxConnections: 1, powered: false, progress: 0 }
|
||||||
|
},
|
||||||
|
stone: {
|
||||||
|
cost: 1e7,
|
||||||
|
name: "Energizer",
|
||||||
|
type: "passive",
|
||||||
|
state: "stone"
|
||||||
|
},
|
||||||
|
coal: {
|
||||||
|
cost: 1e8,
|
||||||
|
name: "Tool Empowerer",
|
||||||
|
type: "empowerer",
|
||||||
|
state: { tools: [], maxConnections: 1, powered: false }
|
||||||
|
},
|
||||||
|
copper: {
|
||||||
|
cost: 1e9,
|
||||||
|
name: "Book",
|
||||||
|
type: "passive",
|
||||||
|
state: "copper"
|
||||||
|
},
|
||||||
|
iron: {
|
||||||
|
cost: 1e10,
|
||||||
|
name: "Portal Generator",
|
||||||
|
type: "portalGenerator",
|
||||||
|
state: { tier: undefined, influences: [] }
|
||||||
|
},
|
||||||
|
silver: {
|
||||||
|
cost: 1e12,
|
||||||
|
name: "Robotics",
|
||||||
|
type: "passive",
|
||||||
|
state: "silver"
|
||||||
|
},
|
||||||
|
gold: {
|
||||||
|
cost: 1e15,
|
||||||
|
name: "Booster",
|
||||||
|
type: "booster",
|
||||||
|
state: { portals: [], maxConnections: 1, powered: false, level: 1 }
|
||||||
|
},
|
||||||
|
emerald: {
|
||||||
|
cost: 1e19,
|
||||||
|
name: "Artificial Intelligence",
|
||||||
|
type: "passive",
|
||||||
|
state: "emerald"
|
||||||
|
},
|
||||||
|
platinum: {
|
||||||
|
cost: 1e24,
|
||||||
|
name: "Upgrader",
|
||||||
|
type: "upgrader",
|
||||||
|
state: { portals: [], maxConnections: 1, powered: false }
|
||||||
|
},
|
||||||
|
diamond: {
|
||||||
|
cost: 1e30,
|
||||||
|
name: "Machine Learning",
|
||||||
|
type: "passive",
|
||||||
|
state: "diamond"
|
||||||
|
},
|
||||||
|
berylium: {
|
||||||
|
cost: 1e37,
|
||||||
|
name: "Automator",
|
||||||
|
type: "automator",
|
||||||
|
state: { portals: [], maxConnections: 1, powered: false }
|
||||||
|
},
|
||||||
|
unobtainium: {
|
||||||
|
cost: 1e45,
|
||||||
|
name: "National Grid",
|
||||||
|
type: "passive",
|
||||||
|
state: "unobtainium"
|
||||||
|
},
|
||||||
|
ultimatum: {
|
||||||
|
cost: 1e54,
|
||||||
|
name: "Investments",
|
||||||
|
type: "investments",
|
||||||
|
state: { portals: [], maxConnections: 1, powered: false }
|
||||||
|
}
|
||||||
|
} as const satisfies Record<
|
||||||
|
Resources,
|
||||||
|
{
|
||||||
|
cost: DecimalSource;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
state?: State;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const relics = {
|
||||||
|
dirt: "Replicator",
|
||||||
|
sand: "Metal Detector",
|
||||||
|
gravel: "Neural Networks",
|
||||||
|
wood: "Mining Laser",
|
||||||
|
stone: "BOGO Coupon",
|
||||||
|
coal: "Planar Intelligence",
|
||||||
|
copper: "Efficient Code",
|
||||||
|
iron: "Trade Agreements",
|
||||||
|
silver: "Machine Synergizer",
|
||||||
|
gold: "XP Market",
|
||||||
|
emerald: "Efficient Portals",
|
||||||
|
platinum: "Time Dilation",
|
||||||
|
diamond: "Paypal",
|
||||||
|
berylium: "Tiered Mining",
|
||||||
|
unobtainium: "Overclocked Portals",
|
||||||
|
ultimatum: "Rebates"
|
||||||
|
} as const satisfies Record<Resources, string>;
|
||||||
|
|
||||||
|
export const passives = {
|
||||||
|
dirt: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "Quadruples mining speed" : "Doubles mining speed"
|
||||||
|
},
|
||||||
|
gravel: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "Quadruples mine ore drops" : "Doubles mine ore drops"
|
||||||
|
},
|
||||||
|
stone: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "Quadruples energy gain" : "Doubles energy gain"
|
||||||
|
},
|
||||||
|
copper: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "Material level is 20% stronger" : "Material level is 10% stronger"
|
||||||
|
},
|
||||||
|
silver: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered
|
||||||
|
? "Doubles each plane's resource gain"
|
||||||
|
: "Quadruples each plane's resource gain"
|
||||||
|
},
|
||||||
|
diamond: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered
|
||||||
|
? "+20% plane's resource gain per upgrade bought"
|
||||||
|
: "+10% plane's resource gain per upgrade bought"
|
||||||
|
},
|
||||||
|
emerald: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered
|
||||||
|
? "+2% plane's resource gain per minute active"
|
||||||
|
: "+1% plane's resource gain per minute active"
|
||||||
|
},
|
||||||
|
unobtainium: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "+2 max connections per machine" : "+1 max connections per machine"
|
||||||
|
},
|
||||||
|
dirtRelic: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "Upgrades apply thrice" : "Upgrades apply twice"
|
||||||
|
},
|
||||||
|
sandRelic: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "Treasure's 2 tiers stronger" : "Treasure's 1 tier stronger"
|
||||||
|
},
|
||||||
|
gravelRelic: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered
|
||||||
|
? "+2% plane's resource gain per repeatable purchase"
|
||||||
|
: "+1% plane's resource gain per repeatable purchase"
|
||||||
|
},
|
||||||
|
woodRelic: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "(Relics)^2 boost mine speed" : "Relics boost mine speed"
|
||||||
|
},
|
||||||
|
stoneRelic: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "2 free levels for repeatables" : "1 free level for repeatables"
|
||||||
|
},
|
||||||
|
coalRelic: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "(Treasures)^2 boost planar speed" : "Treasures boost planar speed"
|
||||||
|
},
|
||||||
|
copperRelic: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "Power 2 machines free" : "Power 1 machine free"
|
||||||
|
},
|
||||||
|
ironRelic: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "Conversions give triple output" : "Conversions give double output"
|
||||||
|
},
|
||||||
|
silverRelic: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "(Power machines)^2 boost ore dropped" : "Power machines boost ore dropped"
|
||||||
|
},
|
||||||
|
goldRelic: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "Each treasure quadruples XP gain" : "Each treasure doubles XP gain"
|
||||||
|
},
|
||||||
|
emeraldRelic: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered
|
||||||
|
? "Creating portals costs a third the energy"
|
||||||
|
: "Creating portals costs half the energy"
|
||||||
|
},
|
||||||
|
platinumRelic: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "Triple dimensions' tick rate" : "Double dimensions' tick rate"
|
||||||
|
},
|
||||||
|
diamondRelic: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "Repeatables/dimensions buy max at once" : "Repeatables buy max at once"
|
||||||
|
},
|
||||||
|
beryliumRelic: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered ? "ln(energy) boosts planar speed" : "log(energy) boosts planar speed"
|
||||||
|
},
|
||||||
|
unobtainiumRelic: {
|
||||||
|
description: (empowered: boolean) =>
|
||||||
|
empowered
|
||||||
|
? "Upgrades/repeatables/dimensions/prestige no longer spend on purchase"
|
||||||
|
: "Upgrades/repeatables no longer spend on purchase"
|
||||||
|
}
|
||||||
|
} as const satisfies Record<string, { description: (empowered: boolean) => string }>;
|
||||||
|
|
||||||
|
export type Passives = keyof typeof passives;
|
||||||
|
|
||||||
|
export const influences = {
|
||||||
|
increaseResources: {
|
||||||
|
display: "+resource",
|
||||||
|
description: (state: InfluenceState) => {
|
||||||
|
const resources = state.data as Resources[];
|
||||||
|
if (resources.length === 0) {
|
||||||
|
return "Increase resource odds - Drag a resource to me!";
|
||||||
|
}
|
||||||
|
if (resources.length === 1) {
|
||||||
|
return `Increase ${resources[0]}'s odds`;
|
||||||
|
}
|
||||||
|
return `Increase ${resources.length} resources' odds`;
|
||||||
|
},
|
||||||
|
cost: 2,
|
||||||
|
initialData: []
|
||||||
|
},
|
||||||
|
decreaseResources: {
|
||||||
|
display: "-resource",
|
||||||
|
description: (state: InfluenceState) => {
|
||||||
|
const resources = state.data as Resources[];
|
||||||
|
if (resources.length === 0) {
|
||||||
|
return "Decrease resource odds - Drag a resource to me!";
|
||||||
|
}
|
||||||
|
if (resources.length === 1) {
|
||||||
|
return `Decrease ${resources[0]}'s odds`;
|
||||||
|
}
|
||||||
|
return `Decrease ${resources.length} resources' odds`;
|
||||||
|
},
|
||||||
|
cost: 2,
|
||||||
|
initialData: []
|
||||||
|
},
|
||||||
|
increaseLength: {
|
||||||
|
display: "+length",
|
||||||
|
description: "Increase length",
|
||||||
|
cost: 100,
|
||||||
|
initialData: undefined
|
||||||
|
},
|
||||||
|
increaseCaches: {
|
||||||
|
display: "+caches",
|
||||||
|
description: "Increase caches odds",
|
||||||
|
cost: 10,
|
||||||
|
initialData: undefined
|
||||||
|
},
|
||||||
|
increaseGens: {
|
||||||
|
display: "+gens",
|
||||||
|
description: "Increase generators odds",
|
||||||
|
cost: 10,
|
||||||
|
initialData: undefined
|
||||||
|
},
|
||||||
|
increaseInfluences: {
|
||||||
|
display: "+influences",
|
||||||
|
description: "Increase influences odds",
|
||||||
|
cost: 10,
|
||||||
|
initialData: undefined
|
||||||
|
},
|
||||||
|
increaseEnergyMults: {
|
||||||
|
display: "+energy mults",
|
||||||
|
description: "Increase energy mults odds",
|
||||||
|
cost: 10,
|
||||||
|
initialData: undefined
|
||||||
|
},
|
||||||
|
increaseResourceMults: {
|
||||||
|
display: "+resource mults",
|
||||||
|
description: "Increase resource mults odds",
|
||||||
|
cost: 10,
|
||||||
|
initialData: undefined
|
||||||
|
},
|
||||||
|
increaseDiff: {
|
||||||
|
display: "+diff",
|
||||||
|
description: "Increase difficulty/rewards odds",
|
||||||
|
cost: 10,
|
||||||
|
initialData: undefined
|
||||||
|
},
|
||||||
|
decreaseDiff: {
|
||||||
|
display: "-diff",
|
||||||
|
description: "Decrease difficulty/rewards odds",
|
||||||
|
cost: 10,
|
||||||
|
initialData: undefined
|
||||||
|
},
|
||||||
|
increaseRewards: {
|
||||||
|
display: "+rewards",
|
||||||
|
description: "Increase rewards level",
|
||||||
|
cost: 1e4,
|
||||||
|
initialData: undefined
|
||||||
|
},
|
||||||
|
relic: {
|
||||||
|
display: "+relic",
|
||||||
|
description: "Max length/difficulty, add tier-unique relic",
|
||||||
|
cost: 1e6,
|
||||||
|
initialData: undefined
|
||||||
|
}
|
||||||
|
} as const satisfies Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
display: string;
|
||||||
|
description: string | ((state: InfluenceState) => string);
|
||||||
|
cost: DecimalSource;
|
||||||
|
initialData?: State;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
export type Influences = keyof typeof influences;
|
||||||
|
|
||||||
|
export const increaseBoostFormula = Formula.variable(0).add(8).times(2).pow10();
|
681
src/data/nodeTypes.tsx
Normal file
681
src/data/nodeTypes.tsx
Normal file
|
@ -0,0 +1,681 @@
|
||||||
|
import {
|
||||||
|
BoardNode,
|
||||||
|
GenericBoard,
|
||||||
|
NodeTypeOptions,
|
||||||
|
ProgressDisplay,
|
||||||
|
Shape,
|
||||||
|
getUniqueNodeID
|
||||||
|
} from "features/boards/board";
|
||||||
|
import { addLayer, layers } from "game/layers";
|
||||||
|
import player from "game/player";
|
||||||
|
import Decimal from "lib/break_eternity";
|
||||||
|
import { format, formatWhole } from "util/break_eternity";
|
||||||
|
import { camelToTitle } from "util/common";
|
||||||
|
import {
|
||||||
|
canAcceptPortal,
|
||||||
|
canAcceptResource,
|
||||||
|
canAcceptTool,
|
||||||
|
deselectAllAction,
|
||||||
|
getIncreaseConnectionsAction,
|
||||||
|
getResourceLevelProgress,
|
||||||
|
isEmpowered,
|
||||||
|
isPowered,
|
||||||
|
labelForAcceptingPortal,
|
||||||
|
labelForAcceptingResource,
|
||||||
|
labelForAcceptingTool,
|
||||||
|
onDropPortal,
|
||||||
|
onDropResource,
|
||||||
|
onDropTool,
|
||||||
|
togglePoweredAction
|
||||||
|
} from "./boardUtils";
|
||||||
|
import {
|
||||||
|
BoosterState,
|
||||||
|
DowsingState,
|
||||||
|
EmpowererState,
|
||||||
|
InfluenceState,
|
||||||
|
MineState,
|
||||||
|
Passives,
|
||||||
|
PortalGeneratorState,
|
||||||
|
PortalState,
|
||||||
|
QuarryState,
|
||||||
|
ResourceState,
|
||||||
|
Resources,
|
||||||
|
increaseBoostFormula,
|
||||||
|
influences,
|
||||||
|
passives,
|
||||||
|
relics,
|
||||||
|
tools
|
||||||
|
} from "./data";
|
||||||
|
import { GenericPlane, createPlane } from "./planes";
|
||||||
|
import { main } from "./projEntry";
|
||||||
|
|
||||||
|
export const mine = {
|
||||||
|
shape: Shape.Diamond,
|
||||||
|
size: 50,
|
||||||
|
title: "🪨",
|
||||||
|
label: node =>
|
||||||
|
node === main.board.selectedNode.value
|
||||||
|
? { text: "Mining" }
|
||||||
|
: Object.keys(main.resourceNodes.value).length === 0
|
||||||
|
? { text: "Click me!" }
|
||||||
|
: null,
|
||||||
|
actionDistance: Math.PI / 4,
|
||||||
|
actions: [togglePoweredAction],
|
||||||
|
progress: node =>
|
||||||
|
isPowered(node) ? new Decimal((node.state as unknown as MineState).progress).toNumber() : 0,
|
||||||
|
progressDisplay: ProgressDisplay.Outline,
|
||||||
|
progressColor: "var(--accent2)",
|
||||||
|
classes: node => ({
|
||||||
|
running: isPowered(node)
|
||||||
|
}),
|
||||||
|
draggable: true
|
||||||
|
} as NodeTypeOptions;
|
||||||
|
|
||||||
|
export const brokenFactory = {
|
||||||
|
shape: Shape.Diamond,
|
||||||
|
size: 50,
|
||||||
|
title: "🛠️",
|
||||||
|
label: node => (node === main.board.selectedNode.value ? { text: "Broken Forge" } : null),
|
||||||
|
actionDistance: Math.PI / 4,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
id: "repair",
|
||||||
|
icon: "build",
|
||||||
|
tooltip: { text: "Repair - 100 energy" },
|
||||||
|
onClick(node) {
|
||||||
|
if (Decimal.gte(main.energy.value, 100)) {
|
||||||
|
node.type = "factory";
|
||||||
|
main.energy.value = Decimal.sub(main.energy.value, 100);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmationLabel: () =>
|
||||||
|
Decimal.gte(main.energy.value, 1000)
|
||||||
|
? { text: "Tap again to confirm" }
|
||||||
|
: { text: "Cannot afford", color: "var(--danger)" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
draggable: true
|
||||||
|
} as NodeTypeOptions;
|
||||||
|
|
||||||
|
export const factory = {
|
||||||
|
shape: Shape.Diamond,
|
||||||
|
size: 50,
|
||||||
|
title: "🛠️",
|
||||||
|
label: node => {
|
||||||
|
if (node === main.board.selectedNode.value) {
|
||||||
|
return {
|
||||||
|
text:
|
||||||
|
node.state == null
|
||||||
|
? "Forge - Drag a resource to me!"
|
||||||
|
: `Forging ${tools[node.state as Resources].name}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if ((main.board as GenericBoard).draggingNode.value?.type === "resource") {
|
||||||
|
const resource = (
|
||||||
|
(main.board as GenericBoard).draggingNode.value?.state as unknown as ResourceState
|
||||||
|
).type;
|
||||||
|
const text = node.state === resource ? "Disconnect" : tools[resource].name;
|
||||||
|
const color =
|
||||||
|
node.state === resource ||
|
||||||
|
(Decimal.gte(main.energy.value, tools[resource].cost) &&
|
||||||
|
main.toolNodes.value[resource] == null)
|
||||||
|
? "var(--accent2)"
|
||||||
|
: "var(--danger)";
|
||||||
|
return {
|
||||||
|
text,
|
||||||
|
color
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
actionDistance: Math.PI / 4,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
id: "deselect",
|
||||||
|
icon: "close",
|
||||||
|
tooltip: { text: "Disconnect resource" },
|
||||||
|
onClick(node) {
|
||||||
|
node.state = undefined;
|
||||||
|
main.board.selectedAction.value = null;
|
||||||
|
main.board.selectedNode.value = null;
|
||||||
|
},
|
||||||
|
visibility: node => node.state != null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "craft",
|
||||||
|
icon: "done",
|
||||||
|
tooltip: node => ({
|
||||||
|
text: `Forge ${tools[node.state as Resources].name} - ${formatWhole(
|
||||||
|
tools[node.state as Resources].cost
|
||||||
|
)} energy`
|
||||||
|
}),
|
||||||
|
onClick(node) {
|
||||||
|
const tool = tools[node.state as Resources];
|
||||||
|
if (
|
||||||
|
Decimal.gte(main.energy.value, tool.cost) &&
|
||||||
|
main.toolNodes.value[node.state as Resources] == null
|
||||||
|
) {
|
||||||
|
main.energy.value = Decimal.sub(main.energy.value, tool.cost);
|
||||||
|
const newNode = {
|
||||||
|
id: getUniqueNodeID(main.board as GenericBoard),
|
||||||
|
position: { ...node.position },
|
||||||
|
type: tool.type,
|
||||||
|
state: "state" in tool ? tool.state : undefined
|
||||||
|
};
|
||||||
|
main.board.placeInAvailableSpace(newNode);
|
||||||
|
main.board.nodes.value.push(newNode);
|
||||||
|
main.board.selectedAction.value = null;
|
||||||
|
main.board.selectedNode.value = null;
|
||||||
|
node.state = undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fillColor: node =>
|
||||||
|
Decimal.gte(main.energy.value, tools[node.state as Resources].cost) &&
|
||||||
|
main.toolNodes.value[node.state as Resources] == null
|
||||||
|
? "var(--accent2)"
|
||||||
|
: "var(--danger)",
|
||||||
|
visibility: node => node.state != null,
|
||||||
|
confirmationLabel: node =>
|
||||||
|
Decimal.gte(main.energy.value, tools[node.state as Resources].cost)
|
||||||
|
? main.toolNodes.value[node.state as Resources] == null
|
||||||
|
? { text: "Tap again to confirm" }
|
||||||
|
: { text: "Already crafted", color: "var(--danger)" }
|
||||||
|
: { text: "Cannot afford", color: "var(--danger)" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
progress: node =>
|
||||||
|
node.state == null || main.toolNodes.value[node.state as Resources] != null
|
||||||
|
? 0
|
||||||
|
: Decimal.div(main.energy.value, tools[node.state as Resources].cost)
|
||||||
|
.clampMax(1)
|
||||||
|
.toNumber(),
|
||||||
|
progressDisplay: ProgressDisplay.Fill,
|
||||||
|
progressColor: node =>
|
||||||
|
node.state != null && Decimal.gte(main.energy.value, tools[node.state as Resources].cost)
|
||||||
|
? "var(--accent2)"
|
||||||
|
: "var(--foreground)",
|
||||||
|
canAccept(node, otherNode) {
|
||||||
|
return otherNode.type === "resource";
|
||||||
|
},
|
||||||
|
onDrop(node, otherNode) {
|
||||||
|
const droppedType = (otherNode.state as unknown as ResourceState).type;
|
||||||
|
if (node.state === droppedType) {
|
||||||
|
node.state = undefined;
|
||||||
|
} else {
|
||||||
|
node.state = droppedType;
|
||||||
|
}
|
||||||
|
main.board.selectedNode.value = node;
|
||||||
|
},
|
||||||
|
draggable: true
|
||||||
|
} as NodeTypeOptions;
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
shape: Shape.Circle,
|
||||||
|
size: 50,
|
||||||
|
title: node => camelToTitle((node.state as unknown as ResourceState).type),
|
||||||
|
subtitle: node => formatWhole((node.state as unknown as ResourceState).amount),
|
||||||
|
progress: node => getResourceLevelProgress((node.state as unknown as ResourceState).type),
|
||||||
|
// Make clicking resources a no-op so they can't be selected
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
onClick() {},
|
||||||
|
progressDisplay: ProgressDisplay.Outline,
|
||||||
|
progressColor: "var(--accent3)",
|
||||||
|
classes: node => ({
|
||||||
|
"affected-node":
|
||||||
|
(main.dowsing.value != null &&
|
||||||
|
isPowered(main.dowsing.value) &&
|
||||||
|
(main.dowsing.value.state as unknown as DowsingState).resources.includes(
|
||||||
|
(node.state as unknown as ResourceState).type
|
||||||
|
)) ||
|
||||||
|
Decimal.neq(
|
||||||
|
main.planarMultis.value[(node.state as unknown as ResourceState).type] ?? 1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
draggable: true
|
||||||
|
} as NodeTypeOptions;
|
||||||
|
|
||||||
|
export const passive = {
|
||||||
|
shape: Shape.Circle,
|
||||||
|
size: 50,
|
||||||
|
title: node => tools[node.state as Resources].name,
|
||||||
|
label: node =>
|
||||||
|
node === main.board.selectedNode.value
|
||||||
|
? {
|
||||||
|
text: passives[node.state as Passives].description(
|
||||||
|
isEmpowered(node.state as Passives)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
outlineColor: "var(--bought)",
|
||||||
|
classes: node => ({
|
||||||
|
"affected-node": isEmpowered(node.state as Passives)
|
||||||
|
}),
|
||||||
|
draggable: true
|
||||||
|
} as NodeTypeOptions;
|
||||||
|
|
||||||
|
export const dowsing = {
|
||||||
|
shape: Shape.Diamond,
|
||||||
|
size: 50,
|
||||||
|
title: "🥢",
|
||||||
|
label: node => {
|
||||||
|
if (node === main.board.selectedNode.value) {
|
||||||
|
return {
|
||||||
|
text:
|
||||||
|
(node.state as unknown as DowsingState).resources.length === 0
|
||||||
|
? "Dowsing - Drag a resource to me!"
|
||||||
|
: `Dowsing (${
|
||||||
|
(node.state as { resources: Resources[] }).resources.length
|
||||||
|
}/${Decimal.add(
|
||||||
|
(node.state as { maxConnections: number }).maxConnections,
|
||||||
|
main.computedBonusConnectionsModifier.value
|
||||||
|
)})`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return labelForAcceptingResource(node, resource => `Double ${resource} odds`);
|
||||||
|
},
|
||||||
|
actionDistance: Math.PI / 4,
|
||||||
|
actions: [
|
||||||
|
deselectAllAction,
|
||||||
|
getIncreaseConnectionsAction(x => x.add(2).pow_base(100), 16),
|
||||||
|
togglePoweredAction
|
||||||
|
],
|
||||||
|
classes: node => ({
|
||||||
|
running: isPowered(node)
|
||||||
|
}),
|
||||||
|
canAccept: canAcceptResource,
|
||||||
|
onDrop: onDropResource,
|
||||||
|
draggable: true
|
||||||
|
} as NodeTypeOptions;
|
||||||
|
|
||||||
|
export const quarry = {
|
||||||
|
shape: Shape.Diamond,
|
||||||
|
size: 50,
|
||||||
|
title: "⛏️",
|
||||||
|
label: node => {
|
||||||
|
if (node === main.board.selectedNode.value) {
|
||||||
|
return {
|
||||||
|
text:
|
||||||
|
(node.state as unknown as DowsingState).resources.length === 0
|
||||||
|
? "Quarry - Drag a resource to me!"
|
||||||
|
: `Quarrying (${
|
||||||
|
(node.state as { resources: Resources[] }).resources.length
|
||||||
|
}/${Decimal.add(
|
||||||
|
(node.state as { maxConnections: number }).maxConnections,
|
||||||
|
main.computedBonusConnectionsModifier.value
|
||||||
|
)})`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return labelForAcceptingResource(
|
||||||
|
node,
|
||||||
|
resource =>
|
||||||
|
`Gather ${format(
|
||||||
|
Decimal.div(main.dropRates[resource].computedModifier.value, 100)
|
||||||
|
)} ${resource}/s`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
actionDistance: Math.PI / 4,
|
||||||
|
actions: [
|
||||||
|
deselectAllAction,
|
||||||
|
getIncreaseConnectionsAction(x => x.add(2).pow_base(10000), 16),
|
||||||
|
togglePoweredAction
|
||||||
|
],
|
||||||
|
progress: node =>
|
||||||
|
isPowered(node)
|
||||||
|
? Decimal.eq(main.quarryProgressRequired.value, 0)
|
||||||
|
? 0
|
||||||
|
: new Decimal((node.state as unknown as QuarryState).progress)
|
||||||
|
.div(main.quarryProgressRequired.value)
|
||||||
|
.toNumber()
|
||||||
|
: 0,
|
||||||
|
progressDisplay: ProgressDisplay.Outline,
|
||||||
|
progressColor: "var(--accent2)",
|
||||||
|
canAccept: canAcceptResource,
|
||||||
|
onDrop: onDropResource,
|
||||||
|
classes: node => ({
|
||||||
|
running: isPowered(node)
|
||||||
|
}),
|
||||||
|
draggable: true
|
||||||
|
} as NodeTypeOptions;
|
||||||
|
|
||||||
|
export const empowerer = {
|
||||||
|
shape: Shape.Diamond,
|
||||||
|
size: 50,
|
||||||
|
title: "🔌",
|
||||||
|
label: node => {
|
||||||
|
if (node === main.board.selectedNode.value) {
|
||||||
|
return {
|
||||||
|
text:
|
||||||
|
(node.state as unknown as EmpowererState).tools.length === 0
|
||||||
|
? "Empowerer - Drag a tool to me!"
|
||||||
|
: `Empowering (${
|
||||||
|
(node.state as { tools: Passives[] }).tools.length
|
||||||
|
}/${Decimal.add(
|
||||||
|
(node.state as { maxConnections: number }).maxConnections,
|
||||||
|
main.computedBonusConnectionsModifier.value
|
||||||
|
)})`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return labelForAcceptingTool(node, passive => {
|
||||||
|
if (passive.includes("Relic")) {
|
||||||
|
return `Double ${relics[passive.slice(0, -5) as Resources]}'s effect`;
|
||||||
|
}
|
||||||
|
return `Double ${tools[passive as Resources].name}'s effect`;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
actionDistance: Math.PI / 4,
|
||||||
|
actions: [
|
||||||
|
deselectAllAction,
|
||||||
|
getIncreaseConnectionsAction(x => x.add(3).pow_base(1000), 16),
|
||||||
|
togglePoweredAction
|
||||||
|
],
|
||||||
|
canAccept: canAcceptTool,
|
||||||
|
onDrop: onDropTool,
|
||||||
|
classes: node => ({
|
||||||
|
running: isPowered(node)
|
||||||
|
}),
|
||||||
|
draggable: true
|
||||||
|
} as NodeTypeOptions;
|
||||||
|
|
||||||
|
export const portalGenerator = {
|
||||||
|
shape: Shape.Diamond,
|
||||||
|
size: 50,
|
||||||
|
title: "⛩️",
|
||||||
|
label: node => {
|
||||||
|
if (node === main.board.selectedNode.value) {
|
||||||
|
return {
|
||||||
|
text:
|
||||||
|
(node.state as unknown as PortalGeneratorState).tier == null
|
||||||
|
? "Portal Spawner - Drag a resource to me!"
|
||||||
|
: `Spawning ${
|
||||||
|
(node.state as unknown as PortalGeneratorState).tier
|
||||||
|
}-tier portal`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const draggingNode = (main.board as GenericBoard).draggingNode.value;
|
||||||
|
if (draggingNode?.type === "resource") {
|
||||||
|
const resource = (draggingNode.state as unknown as ResourceState).type;
|
||||||
|
const text =
|
||||||
|
(node.state as unknown as PortalGeneratorState).tier === resource
|
||||||
|
? "Disconnect"
|
||||||
|
: `${camelToTitle(resource)}-tier Portal`;
|
||||||
|
return {
|
||||||
|
text,
|
||||||
|
color: "var(--accent2)"
|
||||||
|
};
|
||||||
|
} else if (draggingNode?.type === "influence") {
|
||||||
|
const influence = (draggingNode.state as unknown as InfluenceState).type;
|
||||||
|
const { influences } = node.state as unknown as PortalGeneratorState;
|
||||||
|
if (influences.includes(influence)) {
|
||||||
|
return { text: "Disconnect", color: "var(--accent2)" };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
text: "Add influence",
|
||||||
|
color: "var(--accent2)"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
actionDistance: Math.PI / 4,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
id: "deselect",
|
||||||
|
icon: "close",
|
||||||
|
tooltip: { text: "Disconnect all" },
|
||||||
|
onClick(node: BoardNode) {
|
||||||
|
node.state = {
|
||||||
|
...(node.state as object),
|
||||||
|
tier: undefined,
|
||||||
|
influences: []
|
||||||
|
};
|
||||||
|
main.board.selectedAction.value = null;
|
||||||
|
main.board.selectedNode.value = null;
|
||||||
|
},
|
||||||
|
visibility: (node: BoardNode) => {
|
||||||
|
const { tier, influences } = node.state as unknown as PortalGeneratorState;
|
||||||
|
return tier != null || influences.length > 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "makePortal",
|
||||||
|
icon: "done",
|
||||||
|
tooltip: node => ({
|
||||||
|
text: `Spawn ${(node.state as unknown as PortalGeneratorState).tier}-tier portal`
|
||||||
|
}),
|
||||||
|
onClick(node) {
|
||||||
|
let id = 0;
|
||||||
|
while (`portal-${id}` in layers) {
|
||||||
|
id++;
|
||||||
|
}
|
||||||
|
const { tier, influences } = node.state as unknown as PortalGeneratorState;
|
||||||
|
addLayer(
|
||||||
|
createPlane(
|
||||||
|
`portal-${id}`,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
tier!,
|
||||||
|
Math.floor(Math.random() * 4294967296),
|
||||||
|
influences.map(
|
||||||
|
influence =>
|
||||||
|
main.influenceNodes.value[influence]
|
||||||
|
.state as unknown as InfluenceState
|
||||||
|
)
|
||||||
|
),
|
||||||
|
player
|
||||||
|
);
|
||||||
|
const newNode = {
|
||||||
|
id: getUniqueNodeID(main.board as GenericBoard),
|
||||||
|
position: { ...node.position },
|
||||||
|
type: "portal",
|
||||||
|
state: { id: `portal-${id}`, powered: false }
|
||||||
|
};
|
||||||
|
main.board.placeInAvailableSpace(newNode);
|
||||||
|
main.board.nodes.value.push(newNode);
|
||||||
|
main.board.selectedAction.value = null;
|
||||||
|
main.board.selectedNode.value = null;
|
||||||
|
node.state = { tier: undefined, influences: [] };
|
||||||
|
},
|
||||||
|
visibility: node => (node.state as unknown as PortalGeneratorState).tier != null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
canAccept(node, otherNode) {
|
||||||
|
return otherNode.type === "resource" || otherNode.type === "influence";
|
||||||
|
},
|
||||||
|
onDrop(node, otherNode) {
|
||||||
|
if (otherNode.type === "resource") {
|
||||||
|
const droppedType = (otherNode.state as unknown as ResourceState).type;
|
||||||
|
const currentType = (node.state as unknown as PortalGeneratorState).tier;
|
||||||
|
node.state = {
|
||||||
|
...(node.state as object),
|
||||||
|
tier: droppedType === currentType ? undefined : droppedType
|
||||||
|
};
|
||||||
|
} else if (otherNode.type === "influence") {
|
||||||
|
const droppedInfluence = (otherNode.state as unknown as InfluenceState).type;
|
||||||
|
const currentInfluences = (node.state as unknown as PortalGeneratorState).influences;
|
||||||
|
if (currentInfluences.includes(droppedInfluence)) {
|
||||||
|
node.state = {
|
||||||
|
...(node.state as object),
|
||||||
|
influences: currentInfluences.filter(i => i !== droppedInfluence)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
node.state = {
|
||||||
|
...(node.state as object),
|
||||||
|
influences: [...currentInfluences, droppedInfluence]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main.board.selectedNode.value = node;
|
||||||
|
},
|
||||||
|
draggable: true
|
||||||
|
} as NodeTypeOptions;
|
||||||
|
|
||||||
|
export const portal = {
|
||||||
|
shape: Shape.Diamond,
|
||||||
|
size: 50,
|
||||||
|
title: "🌀",
|
||||||
|
label: node =>
|
||||||
|
node === main.board.selectedNode.value
|
||||||
|
? {
|
||||||
|
text: `Portal to ${
|
||||||
|
(layers[(node.state as unknown as PortalState).id] as GenericPlane).name
|
||||||
|
}`,
|
||||||
|
color: (layers[(node.state as unknown as PortalState).id] as GenericPlane).color
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
actionDistance: Math.PI / 4,
|
||||||
|
actions: [togglePoweredAction],
|
||||||
|
classes: node => ({
|
||||||
|
running: isPowered(node),
|
||||||
|
showNotif: (layers[(node.state as unknown as PortalState).id] as GenericPlane).showNotif
|
||||||
|
.value
|
||||||
|
}),
|
||||||
|
outlineColor: node =>
|
||||||
|
(layers[(node.state as unknown as PortalState).id] as GenericPlane).background,
|
||||||
|
draggable: true
|
||||||
|
} as NodeTypeOptions;
|
||||||
|
|
||||||
|
export const influence = {
|
||||||
|
shape: node =>
|
||||||
|
(node.state as unknown as InfluenceState).type === "increaseResources" ||
|
||||||
|
(node.state as unknown as InfluenceState).type === "decreaseResources"
|
||||||
|
? Shape.Diamond
|
||||||
|
: Shape.Circle,
|
||||||
|
size: 50,
|
||||||
|
title: node => influences[(node.state as unknown as InfluenceState).type].display,
|
||||||
|
label: node => {
|
||||||
|
if (node === main.board.selectedNode.value) {
|
||||||
|
const state = node.state as unknown as InfluenceState;
|
||||||
|
const desc = influences[state.type].description;
|
||||||
|
return { text: typeof desc === "function" ? desc(state) : desc };
|
||||||
|
}
|
||||||
|
const draggingNode = (main.board as GenericBoard).draggingNode.value;
|
||||||
|
if (draggingNode?.type === "resource") {
|
||||||
|
const resource = (draggingNode.state as unknown as ResourceState).type;
|
||||||
|
const { type, data } = node.state as unknown as InfluenceState;
|
||||||
|
let text;
|
||||||
|
if (Array.isArray(data) && data.includes(resource)) {
|
||||||
|
text = "Disconnect";
|
||||||
|
} else if (type === "increaseResources") {
|
||||||
|
text = `Increase ${camelToTitle(resource)} odds`;
|
||||||
|
} else if (type === "decreaseResources") {
|
||||||
|
text = `Decrease ${camelToTitle(resource)} odds`;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
text,
|
||||||
|
color: "var(--accent2)"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
actionDistance: Math.PI / 4,
|
||||||
|
actions: [deselectAllAction],
|
||||||
|
canAccept: (node, otherNode) => {
|
||||||
|
if (otherNode.type !== "resource") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Array.isArray((node.state as unknown as InfluenceState).data);
|
||||||
|
},
|
||||||
|
onDrop: (node, otherNode) => {
|
||||||
|
if (otherNode.type !== "resource") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const resource = (otherNode.state as unknown as ResourceState).type;
|
||||||
|
const resources = (node.state as unknown as InfluenceState).data as Resources[] | undefined;
|
||||||
|
if (resources == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (resources.includes(resource)) {
|
||||||
|
node.state = {
|
||||||
|
...(node.state as object),
|
||||||
|
data: resources.filter(r => r !== resource)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
node.state = { ...(node.state as object), data: [...resources, resource] };
|
||||||
|
}
|
||||||
|
main.board.selectedNode.value = node;
|
||||||
|
},
|
||||||
|
outlineColor: "var(--danger)",
|
||||||
|
draggable: true
|
||||||
|
} as NodeTypeOptions;
|
||||||
|
|
||||||
|
export const booster = {
|
||||||
|
shape: Shape.Diamond,
|
||||||
|
size: 50,
|
||||||
|
title: "⌛",
|
||||||
|
label: node => {
|
||||||
|
if (node === main.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,
|
||||||
|
main.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: [] };
|
||||||
|
main.board.selectedAction.value = null;
|
||||||
|
main.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(
|
||||||
|
main.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(main.energy.value, cost)) {
|
||||||
|
main.energy.value = Decimal.sub(main.energy.value, cost);
|
||||||
|
}
|
||||||
|
node.state = {
|
||||||
|
...(node.state as object),
|
||||||
|
level: Decimal.add((node.state as unknown as BoosterState).level, 1)
|
||||||
|
};
|
||||||
|
main.board.selectedAction.value = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
togglePoweredAction
|
||||||
|
],
|
||||||
|
canAccept: canAcceptPortal,
|
||||||
|
onDrop: onDropPortal,
|
||||||
|
classes: node => ({
|
||||||
|
running: isPowered(node)
|
||||||
|
}),
|
||||||
|
draggable: true
|
||||||
|
} as NodeTypeOptions;
|
|
@ -37,23 +37,21 @@ 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 { useToast } from "vue-toastification";
|
import { useToast } from "vue-toastification";
|
||||||
import { createCollapsibleModifierSections, createFormulaPreview, estimateTime } from "./common";
|
import { createCollapsibleModifierSections, createFormulaPreview, estimateTime } from "./common";
|
||||||
import type {
|
import { getColor, getName, sfc32 } from "./utils";
|
||||||
BoosterState,
|
import {
|
||||||
|
Resources,
|
||||||
InfluenceState,
|
InfluenceState,
|
||||||
|
resourceNames,
|
||||||
Influences,
|
Influences,
|
||||||
PortalState,
|
PortalState,
|
||||||
ResourceState,
|
ResourceState,
|
||||||
Resources
|
|
||||||
} from "./projEntry";
|
|
||||||
import {
|
|
||||||
hasWon,
|
|
||||||
influences as influenceTypes,
|
|
||||||
main,
|
|
||||||
mineLootTable,
|
mineLootTable,
|
||||||
relics,
|
relics,
|
||||||
resourceNames
|
BoosterState,
|
||||||
} from "./projEntry";
|
tools,
|
||||||
import { getColor, getName, sfc32 } from "./utils";
|
influences as influenceTypes
|
||||||
|
} from "./data";
|
||||||
|
import { main, hasWon } from "./projEntry";
|
||||||
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
|
@ -108,7 +106,7 @@ export function createPlane(
|
||||||
createMultiplicativeModifier(() => ({
|
createMultiplicativeModifier(() => ({
|
||||||
multiplier: () => (main.isEmpowered("silver") ? 4 : 2),
|
multiplier: () => (main.isEmpowered("silver") ? 4 : 2),
|
||||||
description: () =>
|
description: () =>
|
||||||
(main.isEmpowered("silver") ? "Empowered " : "") + main.tools.silver.name,
|
(main.isEmpowered("silver") ? "Empowered " : "") + tools.silver.name,
|
||||||
enabled: () => main.toolNodes.value.silver != null
|
enabled: () => main.toolNodes.value.silver != null
|
||||||
})),
|
})),
|
||||||
createMultiplicativeModifier(() => ({
|
createMultiplicativeModifier(() => ({
|
||||||
|
@ -117,14 +115,14 @@ export function createPlane(
|
||||||
upgrades.filter(u => u.bought.value).length) /
|
upgrades.filter(u => u.bought.value).length) /
|
||||||
10,
|
10,
|
||||||
description: () =>
|
description: () =>
|
||||||
(main.isEmpowered("diamond") ? "Empowered " : "") + main.tools.diamond.name,
|
(main.isEmpowered("diamond") ? "Empowered " : "") + tools.diamond.name,
|
||||||
enabled: () => main.toolNodes.value.diamond != null
|
enabled: () => main.toolNodes.value.diamond != null
|
||||||
})),
|
})),
|
||||||
createMultiplicativeModifier(() => ({
|
createMultiplicativeModifier(() => ({
|
||||||
multiplier: () =>
|
multiplier: () =>
|
||||||
Decimal.div(timeActive.value, 6000).times(main.isEmpowered("emerald") ? 2 : 1),
|
Decimal.div(timeActive.value, 6000).times(main.isEmpowered("emerald") ? 2 : 1),
|
||||||
description: () =>
|
description: () =>
|
||||||
(main.isEmpowered("emerald") ? "Empowered " : "") + main.tools.emerald.name,
|
(main.isEmpowered("emerald") ? "Empowered " : "") + tools.emerald.name,
|
||||||
enabled: () => main.toolNodes.value.emerald != null
|
enabled: () => main.toolNodes.value.emerald != null
|
||||||
}))
|
}))
|
||||||
]);
|
]);
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue