mirror of
https://github.com/thepaperpilot/Planar-Pioneers.git
synced 2024-11-21 16:13:54 +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 { useToast } from "vue-toastification";
|
||||
import { createCollapsibleModifierSections, createFormulaPreview, estimateTime } from "./common";
|
||||
import type {
|
||||
BoosterState,
|
||||
import { getColor, getName, sfc32 } from "./utils";
|
||||
import {
|
||||
Resources,
|
||||
InfluenceState,
|
||||
resourceNames,
|
||||
Influences,
|
||||
PortalState,
|
||||
ResourceState,
|
||||
Resources
|
||||
} from "./projEntry";
|
||||
import {
|
||||
hasWon,
|
||||
influences as influenceTypes,
|
||||
main,
|
||||
mineLootTable,
|
||||
relics,
|
||||
resourceNames
|
||||
} from "./projEntry";
|
||||
import { getColor, getName, sfc32 } from "./utils";
|
||||
BoosterState,
|
||||
tools,
|
||||
influences as influenceTypes
|
||||
} from "./data";
|
||||
import { main, hasWon } from "./projEntry";
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
|
@ -108,7 +106,7 @@ export function createPlane(
|
|||
createMultiplicativeModifier(() => ({
|
||||
multiplier: () => (main.isEmpowered("silver") ? 4 : 2),
|
||||
description: () =>
|
||||
(main.isEmpowered("silver") ? "Empowered " : "") + main.tools.silver.name,
|
||||
(main.isEmpowered("silver") ? "Empowered " : "") + tools.silver.name,
|
||||
enabled: () => main.toolNodes.value.silver != null
|
||||
})),
|
||||
createMultiplicativeModifier(() => ({
|
||||
|
@ -117,14 +115,14 @@ export function createPlane(
|
|||
upgrades.filter(u => u.bought.value).length) /
|
||||
10,
|
||||
description: () =>
|
||||
(main.isEmpowered("diamond") ? "Empowered " : "") + main.tools.diamond.name,
|
||||
(main.isEmpowered("diamond") ? "Empowered " : "") + tools.diamond.name,
|
||||
enabled: () => main.toolNodes.value.diamond != null
|
||||
})),
|
||||
createMultiplicativeModifier(() => ({
|
||||
multiplier: () =>
|
||||
Decimal.div(timeActive.value, 6000).times(main.isEmpowered("emerald") ? 2 : 1),
|
||||
description: () =>
|
||||
(main.isEmpowered("emerald") ? "Empowered " : "") + main.tools.emerald.name,
|
||||
(main.isEmpowered("emerald") ? "Empowered " : "") + tools.emerald.name,
|
||||
enabled: () => main.toolNodes.value.emerald != null
|
||||
}))
|
||||
]);
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue