From 0f68a188c9829f52368ebdcb15a55c0bc9ddea0c Mon Sep 17 00:00:00 2001 From: thepaperpilot Date: Fri, 21 Apr 2023 19:50:13 -0500 Subject: [PATCH] Setup basic mining mechanic --- .vscode/settings.json | 4 +- src/data/layers/prestige.tsx | 72 ----------- src/data/projEntry.tsx | 240 ++++++++++++++++++++++++++++------- src/data/projInfo.json | 8 +- 4 files changed, 200 insertions(+), 124 deletions(-) delete mode 100644 src/data/layers/prestige.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json index d46602a..0bc567b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,5 +8,7 @@ "[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, - "typescript.tsdk": "node_modules/typescript/lib" + "typescript.tsdk": "node_modules/typescript/lib", + "volar.autoCompleteRefs": false, + "emmet.showExpandedAbbreviation": "never" } diff --git a/src/data/layers/prestige.tsx b/src/data/layers/prestige.tsx deleted file mode 100644 index 30fe99f..0000000 --- a/src/data/layers/prestige.tsx +++ /dev/null @@ -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(0, "prestige points"); - - const conversion = createCumulativeConversion(() => ({ - formula: x => x.div(10).sqrt(), - baseResource: main.points, - gainResource: points - })); - - const reset = createReset(() => ({ - thingsToReset: (): Record[] => [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(() => ( - <> - - {render(resetButton)} - - )), - treeNode, - hotkey - }; -}); - -export default layer; diff --git a/src/data/projEntry.tsx b/src/data/projEntry.tsx index e4640b6..6a42ed7 100644 --- a/src/data/projEntry.tsx +++ b/src/data/projEntry.tsx @@ -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 { 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 MainDisplay from "features/resources/MainDisplay.vue"; +import { createResource } from "features/resources/resource"; +import Formula, { calculateCost } from "game/formulas/formulas"; import type { BaseLayer, GenericLayer } from "game/layers"; import { createLayer } from "game/layers"; +import { State } from "game/persistence"; import type { Player } from "game/player"; import player from "game/player"; -import type { DecimalSource } from "util/bignum"; -import Decimal, { format, formatTime } from "util/bignum"; +import Decimal, { DecimalSource } from "lib/break_eternity"; +import { format, formatTime } from "util/bignum"; import { render } from "util/vue"; -import { computed, toRaw } from "vue"; -import prestige from "./layers/prestige"; +import { ComputedRef, computed, reactive } from "vue"; + +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 */ export const main = createLayer("main", function (this: BaseLayer) { - const points = createResource(10); - const best = trackBest(points); - const total = trackTotal(points); + const energy = createResource(0, "energy"); - 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 resourceLevelFormula = Formula.variable(0).add(1); + function getResourceLevel(amount: DecimalSource) { + const currentLevel = Decimal.floor( + resourceLevelFormula.invertIntegral( + Decimal.add(amount, resourceLevelFormula.evaluateIntegral()) + ) + ); + // TODO sum last x purchases? + 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(() => ({ - nodes: [[prestige.treeNode]], - branches: [], - onReset() { - points.value = toRaw(this.resettingNode.value) === toRaw(prestige.treeNode) ? 0 : 10; - best.value = points.value; - total.value = points.value; + const resourceMinedCooldown: Partial> = reactive({}); + + const board = createBoard(board => ({ + startNodes: () => [{ position: { x: 0, y: 0 }, type: "mine", state: 0 }], + types: { + mine: { + 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)" + }, + resource: { + 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 + } }, - resetPropagation: branchedResetPropagation - })) as GenericTree; + 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> = computed(() => + board.nodes.value.reduce((acc, curr) => { + if (curr.type === "resource") { + acc[(curr.state as unknown as ResourceState).type] = curr; + } + return acc; + }, {} as Record) + ); + + 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 { - name: "Tree", - links: tree.links, + name: "World", + board, + energy, display: jsx(() => ( <> {player.devSpeed === 0 ?
Game Paused
: null} @@ -55,21 +212,10 @@ export const main = createLayer("main", function (this: BaseLayer) { {player.offlineTime != null && player.offlineTime !== 0 ? (
Offline Time: {formatTime(player.offlineTime)}
) : null} -
- {Decimal.lt(points.value, "1e1000") ? You have : null} -

{format(points.value)}

- {Decimal.lt(points.value, "1e1e6") ? points : null} -
- {Decimal.gt(pointGain.value, 0) ?
({oomps.value})
: null} - - {render(tree)} + + {render(board)} - )), - points, - best, - total, - oomps, - tree + )) }; }); @@ -80,7 +226,7 @@ export const main = createLayer("main", function (this: BaseLayer) { export const getInitialLayers = ( /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ player: Partial -): Array => [main, prestige]; +): Array => [main]; /** * A computed ref whose value is true whenever the game is over. diff --git a/src/data/projInfo.json b/src/data/projInfo.json index b32ef22..3692770 100644 --- a/src/data/projInfo.json +++ b/src/data/projInfo.json @@ -1,10 +1,10 @@ { "$schema": "./projInfo-schema.json", - "title": "Profectus", - "description": "A project made in Profectus", - "id": "", - "author": "", + "title": "Planar Pioneers", + "description": "A game about controlling and exploiting planes!", + "id": "planar-pioneers", + "author": "thepaperpilot", "discordName": "", "discordLink": "",