mirror of
https://github.com/thepaperpilot/Planar-Pioneers.git
synced 2024-11-21 16:13:54 +00:00
Setup basic mining mechanic
This commit is contained in:
parent
e892341140
commit
0f68a188c9
4 changed files with 200 additions and 124 deletions
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
|
@ -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.
|
||||||
|
|
|
@ -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": "",
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue