Setup basic mining mechanic

This commit is contained in:
thepaperpilot 2023-04-21 19:50:13 -05:00
parent e892341140
commit 0f68a188c9
4 changed files with 200 additions and 124 deletions

View file

@ -8,5 +8,7 @@
"[typescriptreact]": { "[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"typescript.tsdk": "node_modules/typescript/lib" "typescript.tsdk": "node_modules/typescript/lib",
"volar.autoCompleteRefs": false,
"emmet.showExpandedAbbreviation": "never"
} }

View file

@ -1,72 +0,0 @@
/**
* @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
}));
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,
display: jsx(() => (
<>
<MainDisplay resource={points} color={color} />
{render(resetButton)}
</>
)),
treeNode,
hotkey
};
});
export default layer;

View file

@ -1,51 +1,208 @@
import Spacer from "components/layout/Spacer.vue"; import {
BoardNode,
ProgressDisplay,
Shape,
createBoard,
getUniqueNodeID
} from "features/boards/board";
import { jsx } from "features/feature"; import { jsx } from "features/feature";
import { createResource, trackBest, trackOOMPS, trackTotal } from "features/resources/resource"; import MainDisplay from "features/resources/MainDisplay.vue";
import type { GenericTree } from "features/trees/tree"; import { createResource } from "features/resources/resource";
import { branchedResetPropagation, createTree } from "features/trees/tree"; import Formula, { calculateCost } from "game/formulas/formulas";
import { globalBus } from "game/events";
import type { BaseLayer, GenericLayer } from "game/layers"; import type { BaseLayer, GenericLayer } from "game/layers";
import { createLayer } from "game/layers"; import { createLayer } from "game/layers";
import { State } from "game/persistence";
import type { Player } from "game/player"; import type { Player } from "game/player";
import player from "game/player"; import player from "game/player";
import type { DecimalSource } from "util/bignum"; import Decimal, { DecimalSource } from "lib/break_eternity";
import Decimal, { format, formatTime } from "util/bignum"; import { format, formatTime } from "util/bignum";
import { render } from "util/vue"; import { render } from "util/vue";
import { computed, toRaw } from "vue"; import { ComputedRef, computed, reactive } from "vue";
import prestige from "./layers/prestige";
export interface ResourceState {
type: Resources;
amount: DecimalSource;
}
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;
/** /**
* @hidden * @hidden
*/ */
export const main = createLayer("main", function (this: BaseLayer) { export const main = createLayer("main", function (this: BaseLayer) {
const points = createResource<DecimalSource>(10); const energy = createResource<DecimalSource>(0, "energy");
const best = trackBest(points);
const total = trackTotal(points);
const pointGain = computed(() => { const resourceLevelFormula = Formula.variable(0).add(1);
// eslint-disable-next-line prefer-const function getResourceLevel(amount: DecimalSource) {
let gain = new Decimal(1); const currentLevel = Decimal.floor(
return gain; resourceLevelFormula.invertIntegral(
}); Decimal.add(amount, resourceLevelFormula.evaluateIntegral())
globalBus.on("update", diff => { )
points.value = Decimal.add(points.value, Decimal.times(pointGain.value, diff)); );
}); // TODO sum last x purchases?
const oomps = trackOOMPS(points, pointGain); const requiredForCurrentLevel = calculateCost(resourceLevelFormula, currentLevel, true);
const requiredForNextLevel = calculateCost(
resourceLevelFormula,
Decimal.add(currentLevel, 1),
true
);
return Decimal.sub(amount, requiredForCurrentLevel)
.div(Decimal.sub(requiredForNextLevel, requiredForCurrentLevel))
.toNumber();
}
const tree = createTree(() => ({ const resourceMinedCooldown: Partial<Record<Resources, number>> = reactive({});
nodes: [[prestige.treeNode]],
branches: [], const board = createBoard(board => ({
onReset() { startNodes: () => [{ position: { x: 0, y: 0 }, type: "mine", state: 0 }],
points.value = toRaw(this.resettingNode.value) === toRaw(prestige.treeNode) ? 0 : 10; types: {
best.value = points.value; mine: {
total.value = points.value; shape: Shape.Diamond,
size: 50,
title: "Mine",
label: node => (node === board.selectedNode.value ? null : { text: "Click me!" }),
progress: node =>
node == board.selectedNode.value
? new Decimal(node.state as DecimalSource).toNumber()
: 0,
progressDisplay: ProgressDisplay.Outline,
progressColor: "var(--accent2)"
}, },
resetPropagation: branchedResetPropagation resource: {
})) as GenericTree; shape: Shape.Circle,
size: 50,
title: node => (node.state as unknown as ResourceState).type,
progress: node => getResourceLevel((node.state as unknown as ResourceState).amount),
progressDisplay: ProgressDisplay.Outline,
progressColor: "var(--accent3)",
draggable: true
}
},
style: {
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
overflow: "hidden"
},
links() {
const mine = board.nodes.value.find(n => n.type === "mine") as BoardNode;
return Object.keys(resourceMinedCooldown).map(resource => ({
startNode: mine,
endNode: resourceNodes.value[resource as Resources],
stroke: "var(--accent3)",
strokeWidth: 5
}));
}
}));
const resourceNodes: ComputedRef<Record<Resources, BoardNode>> = computed(() =>
board.nodes.value.reduce((acc, curr) => {
if (curr.type === "resource") {
acc[(curr.state as unknown as ResourceState).type] = curr;
}
return acc;
}, {} as Record<Resources, BoardNode>)
);
function grantResource(type: Resources, amount: DecimalSource) {
let node = resourceNodes.value[type];
if (node == null) {
let x = 0;
x = board.nodes.value
.filter(n => n.position.y < 50 && n.position.y > -50)
.reduce((x, node) => Math.max(x, node.position.x + 100), 0);
node = {
id: getUniqueNodeID(board),
position: { x, y: 0 },
type: "resource",
state: { type, amount }
};
board.nodes.value.push(node);
} else {
const state = node.state as unknown as ResourceState;
node.state = {
...state,
amount: Decimal.add(state.amount, amount)
} as unknown as State;
}
}
// Amount of completions that could give you the exact average of each item without any partials
const sumMineWeights = (Object.values(mineLootTable) as number[]).reduce((a, b) => a + b);
const resourceNames = Object.keys(mineLootTable) as Resources[];
this.on("preUpdate", diff => {
Object.keys(resourceMinedCooldown).forEach(resource => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
resourceMinedCooldown[resource as Resources]! -= diff;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if (resourceMinedCooldown[resource as Resources]! <= 0) {
delete resourceMinedCooldown[resource as Resources];
}
});
if (board.selectedNode.value?.type === "mine") {
const mine = board.selectedNode.value;
const progress = Decimal.add(mine.state as DecimalSource, diff);
const completions = progress.floor();
mine.state = Decimal.sub(progress, completions);
const allResourceCompletions = completions.div(sumMineWeights).floor();
if (allResourceCompletions.gt(0)) {
resourceNames.forEach(resource => {
grantResource(
resource as Resources,
Decimal.times(
mineLootTable[resource as Resources] as number,
allResourceCompletions
)
);
resourceMinedCooldown[resource as Resources] = 0.3;
});
}
const remainder = Decimal.sub(completions, allResourceCompletions).toNumber();
for (let i = 0; i < remainder; i++) {
const random = Math.floor(Math.random() * sumMineWeights);
let weight = 0;
for (let i = 0; i < resourceNames.length; i++) {
const resource = resourceNames[i];
weight += mineLootTable[resource];
if (random <= weight) {
grantResource(resource, 1);
resourceMinedCooldown[resource] = 0.3;
break;
}
}
}
}
// TODO increment energy based on its modifier
});
return { return {
name: "Tree", name: "World",
links: tree.links, board,
energy,
display: jsx(() => ( display: jsx(() => (
<> <>
{player.devSpeed === 0 ? <div>Game Paused</div> : null} {player.devSpeed === 0 ? <div>Game Paused</div> : null}
@ -55,21 +212,10 @@ export const main = createLayer("main", function (this: BaseLayer) {
{player.offlineTime != null && player.offlineTime !== 0 ? ( {player.offlineTime != null && player.offlineTime !== 0 ? (
<div>Offline Time: {formatTime(player.offlineTime)}</div> <div>Offline Time: {formatTime(player.offlineTime)}</div>
) : null} ) : null}
<div> <MainDisplay resource={energy} />
{Decimal.lt(points.value, "1e1000") ? <span>You have </span> : null} {render(board)}
<h2>{format(points.value)}</h2>
{Decimal.lt(points.value, "1e1e6") ? <span> points</span> : null}
</div>
{Decimal.gt(pointGain.value, 0) ? <div>({oomps.value})</div> : null}
<Spacer />
{render(tree)}
</> </>
)), ))
points,
best,
total,
oomps,
tree
}; };
}); });
@ -80,7 +226,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
export const getInitialLayers = ( export const getInitialLayers = (
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */ /* eslint-disable-next-line @typescript-eslint/no-unused-vars */
player: Partial<Player> player: Partial<Player>
): Array<GenericLayer> => [main, prestige]; ): Array<GenericLayer> => [main];
/** /**
* A computed ref whose value is true whenever the game is over. * A computed ref whose value is true whenever the game is over.

View file

@ -1,10 +1,10 @@
{ {
"$schema": "./projInfo-schema.json", "$schema": "./projInfo-schema.json",
"title": "Profectus", "title": "Planar Pioneers",
"description": "A project made in Profectus", "description": "A game about controlling and exploiting planes!",
"id": "", "id": "planar-pioneers",
"author": "", "author": "thepaperpilot",
"discordName": "", "discordName": "",
"discordLink": "", "discordLink": "",