Revert changes in /data. Check the board-example branch for them instead

This commit is contained in:
thepaperpilot 2024-03-10 09:27:31 -05:00
parent 99227a2cb2
commit f9c59f7636
2 changed files with 142 additions and 405 deletions

View file

@ -0,0 +1,73 @@
/**
* @module
* @hidden
*/
import { main } from "data/projEntry";
import { createCumulativeConversion } from "features/conversion";
import { jsx } from "features/feature";
import { createHotkey } from "features/hotkey";
import { createReset } from "features/reset";
import MainDisplay from "features/resources/MainDisplay.vue";
import { createResource } from "features/resources/resource";
import { addTooltip } from "features/tooltips/tooltip";
import { createResourceTooltip } from "features/trees/tree";
import { BaseLayer, createLayer } from "game/layers";
import type { DecimalSource } from "util/bignum";
import { render } from "util/vue";
import { createLayerTreeNode, createResetButton } from "../common";
const id = "p";
const layer = createLayer(id, function (this: BaseLayer) {
const name = "Prestige";
const color = "#4BDC13";
const points = createResource<DecimalSource>(0, "prestige points");
const conversion = createCumulativeConversion(() => ({
formula: x => x.div(10).sqrt(),
baseResource: main.points,
gainResource: points
}));
const reset = createReset(() => ({
thingsToReset: (): Record<string, unknown>[] => [layer]
}));
const treeNode = createLayerTreeNode(() => ({
layerID: id,
color,
reset
}));
const tooltip = addTooltip(treeNode, {
display: createResourceTooltip(points),
pinnable: true
});
const resetButton = createResetButton(() => ({
conversion,
tree: main.tree,
treeNode
}));
const hotkey = createHotkey(() => ({
description: "Reset for prestige points",
key: "p",
onPress: resetButton.onClick
}));
return {
name,
color,
points,
tooltip,
display: jsx(() => (
<>
<MainDisplay resource={points} color={color} />
{render(resetButton)}
</>
)),
treeNode,
hotkey
};
});
export default layer;

View file

@ -1,428 +1,92 @@
import Board from "game/boards/Board.vue";
import CircleProgress from "game/boards/CircleProgress.vue";
import SVGNode from "game/boards/SVGNode.vue";
import SquareProgress from "game/boards/SquareProgress.vue";
import {
NodePosition,
makeDraggable,
placeInAvailableSpace,
setupActions,
setupDraggableNode,
setupUniqueIds
} from "game/boards/board";
import Node from "components/Node.vue";
import Spacer from "components/layout/Spacer.vue";
import { jsx } from "features/feature";
import { createResource } from "features/resources/resource";
import { createUpgrade } from "features/upgrades/upgrade";
import { createResource, trackBest, trackOOMPS, trackTotal } from "features/resources/resource";
import type { GenericTree } from "features/trees/tree";
import { branchedResetPropagation, createTree } from "features/trees/tree";
import { globalBus } from "game/events";
import type { BaseLayer, GenericLayer } from "game/layers";
import { createLayer } from "game/layers";
import { Persistent, persistent } from "game/persistence";
import type { Player } from "game/player";
import { createCostRequirement } from "game/requirements";
import player from "game/player";
import type { DecimalSource } from "util/bignum";
import Decimal, { format, formatTime } from "util/bignum";
import { render } from "util/vue";
import { ComponentPublicInstance, computed, ref, watch } from "vue";
import { setupSelectable } from "./common";
import "./common.css";
import { computed, toRaw } from "vue";
import prestige from "./layers/prestige";
/**
* @hidden
*/
export const main = createLayer("main", function (this: BaseLayer) {
type ANode = NodePosition & { id: number; links: number[]; type: "anode"; z: number };
type BNode = NodePosition & { id: number; links: number[]; type: "bnode"; z: number };
type CNode = typeof cNode & { position: Persistent<NodePosition> };
type NodeTypes = ANode | BNode;
const points = createResource<DecimalSource>(10);
const best = trackBest(points);
const total = trackTotal(points);
const board = ref<ComponentPublicInstance<typeof Board>>();
const { select, deselect, selected } = setupSelectable<number>();
const {
select: selectAction,
deselect: deselectAction,
selected: selectedAction
} = setupSelectable<number>();
watch(selected, selected => {
if (selected == null) {
deselectAction();
}
const pointGain = computed(() => {
// eslint-disable-next-line prefer-const
let gain = new Decimal(1);
return gain;
});
globalBus.on("update", diff => {
points.value = Decimal.add(points.value, Decimal.times(pointGain.value, diff));
});
const oomps = trackOOMPS(points, pointGain);
const {
startDrag,
endDrag,
drag,
nodeBeingDragged,
hasDragged,
receivingNodes,
receivingNode,
dragDelta
} = setupDraggableNode<number | "cnode">({
board,
getPosition(id) {
return nodesById.value[id] ?? (cNode as CNode).position.value;
const tree = createTree(() => ({
nodes: [[prestige.treeNode]],
branches: [],
onReset() {
points.value = toRaw(this.resettingNode.value) === toRaw(prestige.treeNode) ? 0 : 10;
best.value = points.value;
total.value = points.value;
},
setPosition(id, position) {
const node = nodesById.value[id] ?? (cNode as CNode).position.value;
node.x = position.x;
node.y = position.y;
}
});
// a nodes can be slotted into b nodes to draw a branch between them, with limited connections
// a nodes can be selected and have an action to spawn a b node, and vice versa
// Newly spawned nodes should find a safe spot to spawn, and display a link to their creator
// a nodes use all the stuff circles used to have, and b diamonds
// c node also exists but is a single Upgrade element that cannot be selected, but can be dragged
// d nodes are a performance test - 1000 simple nodes that have no interactions
// Make all nodes animate in (decorator? `fadeIn(feature)?)
const nodes = persistent<(ANode | BNode)[]>([
{ id: 0, x: 0, y: 0, z: 0, links: [], type: "anode" }
]);
const nodesById = computed<Record<string, NodeTypes>>(() =>
nodes.value.reduce((acc, curr) => ({ ...acc, [curr.id]: curr }), {})
);
function mouseDownNode(e: MouseEvent | TouchEvent, node: NodeTypes) {
const oldZ = node.z;
nodes.value.forEach(node => {
if (node.z > oldZ) {
node.z--;
}
});
node.z = nextId.value;
if (nodeBeingDragged.value == null) {
startDrag(e, node.id);
}
deselect();
}
function mouseUpNode(e: MouseEvent | TouchEvent, node: NodeTypes) {
if (!hasDragged.value) {
endDrag();
if (typeof node.id === "number") {
select(node.id);
}
e.stopPropagation();
}
}
function translate(node: NodePosition, isDragging: boolean) {
let x = node.x;
let y = node.y;
if (isDragging) {
x += dragDelta.value.x;
y += dragDelta.value.y;
}
return ` translate(${x}px,${y}px)`;
}
function rotate(rotation: number) {
return ` rotate(${rotation}deg) `;
}
function scale(nodeOrBool: NodeTypes | boolean) {
const isSelected =
typeof nodeOrBool === "boolean" ? nodeOrBool : selected.value === nodeOrBool.id;
return isSelected ? " scale(1.2)" : "";
}
function opacity(node: NodeTypes) {
const isDragging = selected.value !== node.id && nodeBeingDragged.value === node.id;
if (isDragging) {
return "; opacity: 0.5;";
}
return "";
}
function zIndex(node: NodeTypes) {
if (selected.value === node.id || nodeBeingDragged.value === node.id) {
return "; z-index: 100000000";
}
return "; z-index: " + node.z;
}
const renderANode = function (node: ANode) {
return (
<SVGNode
style={`transform: ${translate(node, nodeBeingDragged.value === node.id)}${opacity(
node
)}${zIndex(node)}`}
onMouseDown={e => mouseDownNode(e, node)}
onMouseUp={e => mouseUpNode(e, node)}
>
<g style={`transform: ${scale(node)}`}>
{receivingNodes.value.includes(node.id) && (
<circle
r="58"
fill="var(--background)"
stroke={receivingNode.value === node.id ? "#0F0" : "#0F03"}
stroke-width="2"
/>
)}
<CircleProgress r={54.5} progress={0.5} stroke="var(--accent2)" />
<circle
r="50"
fill="var(--raised-background)"
stroke="var(--outline)"
stroke-width="4"
/>
</g>
{selected.value === node.id && selectedAction.value === 0 && (
<text y="140" fill="var(--foreground)" class="node-text">
Spawn B Node
</text>
)}
<text fill="var(--foreground)" class="node-text">
A
</text>
</SVGNode>
);
};
const aActions = setupActions({
node: () => nodesById.value[selected.value ?? ""],
shouldShowActions: node => node.type === "anode",
actions(node) {
return [
p => (
<g
style={`transform: ${translate(p, selectedAction.value === 0)}${scale(
selectedAction.value === 0
)}`}
onClick={() => {
if (selectedAction.value === 0) {
spawnBNode(node as ANode);
} else {
selectAction(0);
}
}}
>
<circle fill="black" r="20"></circle>
<text fill="white" class="material-icons" x="-12" y="12">
add
</text>
</g>
)
];
},
distance: 100
});
const sqrtTwo = Math.sqrt(2);
const renderBNode = function (node: BNode) {
return (
<SVGNode
style={`transform: ${translate(node, nodeBeingDragged.value === node.id)}${opacity(
node
)}${zIndex(node)}`}
onMouseDown={e => mouseDownNode(e, node)}
onMouseUp={e => mouseUpNode(e, node)}
>
<g style={`transform: ${scale(node)}${rotate(45)}`}>
{receivingNodes.value.includes(node.id) && (
<rect
width={50 * sqrtTwo + 16}
height={50 * sqrtTwo + 16}
style={`translate(${(-50 * sqrtTwo + 16) / 2}, ${
(-50 * sqrtTwo + 16) / 2
})`}
fill="var(--background)"
stroke={receivingNode.value === node.id ? "#0F0" : "#0F03"}
stroke-width="2"
/>
)}
<SquareProgress
size={50 * sqrtTwo + 9}
progress={0.5}
stroke="var(--accent2)"
/>
<rect
width={50 * sqrtTwo}
height={50 * sqrtTwo}
style={`transform: translate(${(-50 * sqrtTwo) / 2}px, ${
(-50 * sqrtTwo) / 2
}px)`}
fill="var(--raised-background)"
stroke="var(--outline)"
stroke-width="4"
/>
</g>
{selected.value === node.id && selectedAction.value === 0 && (
<text y="140" fill="var(--foreground)" class="node-text">
Spawn A Node
</text>
)}
<text fill="var(--foreground)" class="node-text">
B
</text>
</SVGNode>
);
};
const bActions = setupActions({
node: () => nodesById.value[selected.value ?? ""],
shouldShowActions: node => node.type === "bnode",
actions(node) {
return [
p => (
<g
style={`transform: ${translate(p, selectedAction.value === 0)}${scale(
selectedAction.value === 0
)}`}
onClick={() => {
if (selectedAction.value === 0) {
spawnANode(node as BNode);
} else {
selectAction(0);
}
}}
>
<circle fill="white" r="20"></circle>
<text fill="black" class="material-icons" x="-12" y="12">
add
</text>
</g>
)
];
},
distance: 100
});
function spawnANode(parent: ANode | BNode) {
const node: ANode = {
x: parent.x,
y: parent.y,
z: nextId.value,
type: "anode",
links: [parent.id],
id: nextId.value
};
placeInAvailableSpace(node, nodes.value);
nodes.value.push(node);
}
function spawnBNode(parent: ANode | BNode) {
const node: BNode = {
x: parent.x,
y: parent.y,
z: nextId.value,
type: "bnode",
links: [parent.id],
id: nextId.value
};
placeInAvailableSpace(node, nodes.value);
nodes.value.push(node);
}
const points = createResource(10);
const cNode = createUpgrade(() => ({
display: "<h1>C</h1>",
// Purposefully not using noPersist
requirements: createCostRequirement(() => ({ cost: 10, resource: points })),
style: {
x: "100px",
y: "100px",
pointerEvents: "none"
}
}));
makeDraggable(cNode, {
id: "cnode",
endDrag,
startDrag,
hasDragged,
nodeBeingDragged,
dragDelta,
onMouseUp() {
if (!hasDragged.value) {
cNode.purchase();
}
}
});
const dNodesPerAxis = 50;
const dNodes = jsx(() => (
<>
{new Array(dNodesPerAxis * dNodesPerAxis).fill(0).map((_, i) => {
const x = (Math.floor(i / dNodesPerAxis) - dNodesPerAxis / 2) * 100;
const y = ((i % dNodesPerAxis) - dNodesPerAxis / 2) * 100;
return (
<path
fill="var(--bought)"
style={`transform: translate(${x}px, ${y}px) scale(0.05)`}
d="M62.43,122.88h-1.98c0-16.15-6.04-30.27-18.11-42.34C30.27,68.47,16.16,62.43,0,62.43v-1.98 c16.16,0,30.27-6.04,42.34-18.14C54.41,30.21,60.45,16.1,60.45,0h1.98c0,16.15,6.04,30.27,18.11,42.34 c12.07,12.07,26.18,18.11,42.34,18.11v1.98c-16.15,0-30.27,6.04-42.34,18.11C68.47,92.61,62.43,106.72,62.43,122.88L62.43,122.88z"
/>
);
})}
</>
));
const links = jsx(() => (
<>
{nodes.value
.reduce(
(acc, curr) => [
...acc,
...curr.links.map(l => ({ from: curr, to: nodesById.value[l] }))
],
[] as { from: NodeTypes; to: NodeTypes }[]
)
.map(link => (
<line
stroke="white"
stroke-width={4}
x1={
nodeBeingDragged.value === link.from.id
? dragDelta.value.x + link.from.x
: link.from.x
}
y1={
nodeBeingDragged.value === link.from.id
? dragDelta.value.y + link.from.y
: link.from.y
}
x2={
nodeBeingDragged.value === link.to.id
? dragDelta.value.x + link.to.x
: link.to.x
}
y2={
nodeBeingDragged.value === link.to.id
? dragDelta.value.y + link.to.y
: link.to.y
}
/>
))}
</>
));
const nextId = setupUniqueIds(() => nodes.value);
function renderNode(node: NodeTypes | typeof cNode) {
if (node.type === "anode") {
return renderANode(node);
} else if (node.type === "bnode") {
return renderBNode(node);
} else {
return render(node);
}
}
resetPropagation: branchedResetPropagation
})) as GenericTree;
return {
name: "Tree",
color: "var(--accent1)",
links: tree.links,
display: jsx(() => (
<>
<Board
onDrag={drag}
onMouseDown={deselect}
onMouseUp={endDrag}
onMouseLeave={endDrag}
ref={board}
>
<SVGNode>
{dNodes()}
{links()}
</SVGNode>
{nodes.value.map(renderNode)}
{render(cNode)}
<SVGNode>
{aActions()}
{bActions()}
</SVGNode>
</Board>
{player.devSpeed === 0 ? (
<div>
Game Paused
<Node id="paused" />
</div>
) : null}
{player.devSpeed != null && player.devSpeed !== 0 && player.devSpeed !== 1 ? (
<div>
Dev Speed: {format(player.devSpeed)}x
<Node id="devspeed" />
</div>
) : null}
{player.offlineTime != null && player.offlineTime !== 0 ? (
<div>
Offline Time: {formatTime(player.offlineTime)}
<Node id="offline" />
</div>
) : null}
<div>
{Decimal.lt(points.value, "1e1000") ? <span>You have </span> : null}
<h2>{format(points.value)}</h2>
{Decimal.lt(points.value, "1e1e6") ? <span> points</span> : null}
</div>
{Decimal.gt(pointGain.value, 0) ? (
<div>
({oomps.value})
<Node id="oomps" />
</div>
) : null}
<Spacer />
{render(tree)}
</>
)),
boardNodes: nodes,
cNode,
selected: persistent(selected)
points,
best,
total,
oomps,
tree
};
});
@ -433,7 +97,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
export const getInitialLayers = (
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
player: Partial<Player>
): Array<GenericLayer> => [main];
): Array<GenericLayer> => [main, prestige];
/**
* A computed ref whose value is true whenever the game is over.