Implement influences

This commit is contained in:
thepaperpilot 2023-04-30 15:02:55 -05:00
parent 2a362a95c3
commit 44844f0de7
2 changed files with 339 additions and 66 deletions

View file

@ -2,7 +2,7 @@ import ModalVue from "components/Modal.vue";
import SpacerVue from "components/layout/Spacer.vue"; import SpacerVue from "components/layout/Spacer.vue";
import StickyVue from "components/layout/Sticky.vue"; import StickyVue from "components/layout/Sticky.vue";
import { GenericAchievement, createAchievement } from "features/achievements/achievement"; import { GenericAchievement, createAchievement } from "features/achievements/achievement";
import { BoardNode } from "features/boards/board"; import { BoardNode, getUniqueNodeID } from "features/boards/board";
import { jsx } from "features/feature"; import { jsx } from "features/feature";
import { createRepeatable } from "features/repeatable"; import { createRepeatable } from "features/repeatable";
import { createResource } from "features/resources/resource"; import { createResource } from "features/resources/resource";
@ -18,7 +18,7 @@ import {
createMultiplicativeModifier, createMultiplicativeModifier,
createSequentialModifier createSequentialModifier
} from "game/modifiers"; } from "game/modifiers";
import { noPersist, persistent } from "game/persistence"; import { State, noPersist, persistent } from "game/persistence";
import { createCostRequirement } from "game/requirements"; import { createCostRequirement } from "game/requirements";
import { adjectives, colors, uniqueNamesGenerator } from "unique-names-generator"; import { adjectives, colors, uniqueNamesGenerator } from "unique-names-generator";
import Decimal, { DecimalSource } from "util/bignum"; import Decimal, { DecimalSource } from "util/bignum";
@ -28,7 +28,14 @@ import { Computable, ProcessedComputable, convertComputable } from "util/compute
import { VueFeature, render, renderRow, trackHover } from "util/vue"; import { VueFeature, render, renderRow, trackHover } from "util/vue";
import { ComputedRef, Ref, computed, ref, unref } from "vue"; import { ComputedRef, Ref, computed, ref, unref } from "vue";
import { createCollapsibleModifierSections, createFormulaPreview, estimateTime } from "./common"; import { createCollapsibleModifierSections, createFormulaPreview, estimateTime } from "./common";
import { main, mineLootTable, resourceNames } from "./projEntry"; import {
InfluenceState,
Influences,
influences as influenceTypes,
main,
mineLootTable,
resourceNames
} from "./projEntry";
import type { ResourceState, Resources, PortalState } from "./projEntry"; import type { ResourceState, Resources, PortalState } from "./projEntry";
import { getColor, getName, sfc32 } from "./utils"; import { getColor, getName, sfc32 } from "./utils";
@ -39,7 +46,12 @@ export type Treasure = GenericAchievement & {
resourceMulti: DecimalSource; resourceMulti: DecimalSource;
}; };
export function createPlane(id: string, tier: Resources, seed: number) { export function createPlane(
id: string,
tier: Resources,
seed: number,
influences: InfluenceState[]
) {
return createLayer(id, function (this: BaseLayer) { return createLayer(id, function (this: BaseLayer) {
const random = sfc32(0, seed >> 0, seed >> 32, 1); const random = sfc32(0, seed >> 0, seed >> 32, 1);
for (let i = 0; i < 12; i++) random(); for (let i = 0; i < 12; i++) random();
@ -49,8 +61,21 @@ export function createPlane(id: string, tier: Resources, seed: number) {
const background = getColor([0.18, 0.2, 0.25], random); const background = getColor([0.18, 0.2, 0.25], random);
const resource = createResource<DecimalSource>(0, getName(random)); const resource = createResource<DecimalSource>(0, getName(random));
const tierIndex = resourceNames.indexOf(tier); const tierIndex = resourceNames.indexOf(tier);
let difficultyRand = random();
if (influences.some(i => i.type === "increaseDiff")) {
difficultyRand = difficultyRand / 2 + 0.5;
}
if (influences.some(i => i.type === "decreaseDiff")) {
difficultyRand = difficultyRand / 2;
}
const difficulty = random() + tierIndex + 1; const difficulty = random() + tierIndex + 1;
const length = Math.ceil(random() * (tierIndex + 2)); const rewardsLevel = influences.some(i => i.type === "increaseRewards")
? difficulty + 1
: difficulty;
let length = Math.ceil(random() * (tierIndex + 2));
if (influences.some(i => i.type === "increaseLength")) {
length++;
}
const resourceModifiers: WithRequired<Modifier, "description" | "invert">[] = []; const resourceModifiers: WithRequired<Modifier, "description" | "invert">[] = [];
const resourceGainModifier = createSequentialModifier(() => resourceModifiers); const resourceGainModifier = createSequentialModifier(() => resourceModifiers);
@ -62,21 +87,30 @@ export function createPlane(id: string, tier: Resources, seed: number) {
cost: FormulaSource; cost: FormulaSource;
}[] = []; }[] = [];
function prepareFeature( function prepareFeature({
feature: VueFeature, feature,
canClick: Computable<boolean>, canClick,
modifier: WithRequired<Modifier, "description" | "invert">, modifier,
cost: FormulaSource, cost,
previewModifier: WithRequired<Modifier, "invert"> = modifier previewModifier,
) { showETA
}: {
feature: VueFeature;
canClick: Computable<boolean>;
modifier: WithRequired<Modifier, "description" | "invert">;
cost: FormulaSource;
previewModifier?: WithRequired<Modifier, "invert">;
showETA?: Computable<boolean | undefined>;
}) {
canClick = convertComputable(canClick); canClick = convertComputable(canClick);
showETA = convertComputable(showETA);
const isHovering = trackHover(feature); const isHovering = trackHover(feature);
previews.push({ previews.push({
shouldShowPreview: computed( shouldShowPreview: computed(
() => unref(canClick as ProcessedComputable<boolean>) && isHovering.value () => unref(canClick as ProcessedComputable<boolean>) && isHovering.value
), ),
modifier: previewModifier, modifier: previewModifier ?? modifier,
cost cost
}); });
resourceModifiers.push(modifier); resourceModifiers.push(modifier);
@ -84,7 +118,10 @@ export function createPlane(id: string, tier: Resources, seed: number) {
unrefFormulaSource(cost) unrefFormulaSource(cost)
); );
addTooltip(feature, { addTooltip(feature, {
display: eta, display:
showETA == null
? eta
: () => (unref(showETA as ProcessedComputable<boolean>) ? eta.value : ""),
direction: Direction.Down direction: Direction.Down
}); });
} }
@ -101,10 +138,15 @@ export function createPlane(id: string, tier: Resources, seed: number) {
.times(10) .times(10)
.times(costFormula.evaluate()) .times(costFormula.evaluate())
); );
const influenceTreasures: Influences[] = [];
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
const featureWeights = { const featureWeights = {
upgrades: 16, upgrades: 32,
repeatables: i <= 1 ? 0 : 8 repeatables: i <= 1 ? 0 : 16
// conversion: i <= 3 ? 0 : 8,
// xp: i <= 5 ? 0 : 4,
// dimensions: i <= 7 ? 0 : 2,
// prestige: i <= 7 && i < length - 1 ? 0 : 1
}; };
const type = pickRandom(featureWeights, random); const type = pickRandom(featureWeights, random);
switch (type) { switch (type) {
@ -177,12 +219,13 @@ export function createPlane(id: string, tier: Resources, seed: number) {
}, },
visibility: upgradeVisibility visibility: upgradeVisibility
})); }));
prepareFeature( prepareFeature({
upgrade, feature: upgrade,
() => !upgrade.bought.value && upgrade.canPurchase.value, canClick: () => upgrade.canPurchase.value,
modifier, modifier,
cost cost,
); showETA: () => !upgrade.bought.value
});
upgrades.push(upgrade); upgrades.push(upgrade);
} }
features.push(upgrades); features.push(upgrades);
@ -193,6 +236,7 @@ export function createPlane(id: string, tier: Resources, seed: number) {
const repeatableTypeWeights = { const repeatableTypeWeights = {
add: 1.5, add: 1.5,
mult: 3 mult: 3
// pow was too hard to implement such that the cost would be invertible
}; };
const upgradeType = pickRandom(repeatableTypeWeights, random); const upgradeType = pickRandom(repeatableTypeWeights, random);
// Repeatables will estimate 5 purchases between each increment of `n` // Repeatables will estimate 5 purchases between each increment of `n`
@ -279,23 +323,30 @@ export function createPlane(id: string, tier: Resources, seed: number) {
}), }),
visibility: repeatableVisibility visibility: repeatableVisibility
})); }));
prepareFeature( prepareFeature({
repeatable, feature: repeatable,
() => unref(repeatable.canClick), canClick: () => unref(repeatable.canClick),
modifier, modifier,
cost, cost,
previewModifier previewModifier
); });
repeatables.push(repeatable); repeatables.push(repeatable);
} }
features.push(repeatables); features.push(repeatables);
break; break;
} }
const treasureWeights = { const treasureWeights = {
cache: 100, cache: influences.some(i => i.type === "increaseCaches") ? 10 : 1,
generation: 10, generation: influences.some(i => i.type === "increaseGens") ? 10 : 1,
resourceMulti: 5, resourceMulti: influences.some(i => i.type === "increaseResourceMults") ? 10 : 1,
energyMulti: 5 energyMulti: influences.some(i => i.type === "increaseEnergyMults") ? 2.5 : 0.25,
influences:
Object.keys(main.influenceNodes.value).length + influenceTreasures.length ===
Object.keys(influenceTypes).length
? 0
: influences.some(i => i.type === "increaseInfluences")
? 20
: 2
}; };
const treasureType = pickRandom(treasureWeights, random); const treasureType = pickRandom(treasureWeights, random);
let description = ""; let description = "";
@ -307,8 +358,8 @@ export function createPlane(id: string, tier: Resources, seed: number) {
let resourceMulti: DecimalSource; let resourceMulti: DecimalSource;
switch (treasureType) { switch (treasureType) {
case "cache": case "cache":
randomResource = getRandomResource(random); randomResource = getRandomResource(random, influences);
description = `Gain ${format(difficulty)}x your current ${randomResource}.`; description = `Gain ${format(rewardsLevel)}x your current ${randomResource}.`;
onComplete = () => onComplete = () =>
main.grantResource( main.grantResource(
randomResource, randomResource,
@ -317,29 +368,61 @@ export function createPlane(id: string, tier: Resources, seed: number) {
main.resourceNodes.value[randomResource] main.resourceNodes.value[randomResource]
?.state as unknown as ResourceState | null ?.state as unknown as ResourceState | null
)?.amount ?? 0, )?.amount ?? 0,
difficulty rewardsLevel
) )
); );
break; break;
case "generation": case "generation":
randomResource = getRandomResource(random); randomResource = getRandomResource(random, influences);
const gain = Decimal.div(difficulty, 120).times(mineLootTable[randomResource]); const gain = Decimal.div(rewardsLevel, 120).times(
mineLootTable[randomResource]
);
description = `Gain ${format(gain)} ${randomResource}/s while plane is active.`; description = `Gain ${format(gain)} ${randomResource}/s while plane is active.`;
update = diff => main.grantResource(randomResource, Decimal.times(diff, gain)); update = diff => main.grantResource(randomResource, Decimal.times(diff, gain));
link = computed(() => main.resourceNodes.value[randomResource]); link = computed(() => main.resourceNodes.value[randomResource]);
break; break;
case "resourceMulti": case "resourceMulti":
effectedResource = randomResource = getRandomResource(random); effectedResource = randomResource = getRandomResource(random, influences);
resourceMulti = Decimal.div(difficulty, 17).pow_base(2); resourceMulti = Decimal.div(rewardsLevel, 17).pow_base(2);
description = `Gain ${format( description = `Gain ${format(
resourceMulti resourceMulti
)}x ${randomResource} while plane is active.`; )}x ${randomResource} while plane is active.`;
break; break;
case "energyMulti": case "energyMulti":
effectedResource = "energy"; effectedResource = "energy";
resourceMulti = Decimal.div(difficulty, 17); resourceMulti = Decimal.div(rewardsLevel, 17);
description = `Gain ${format(resourceMulti)}x energy while plane is active.`; description = `Gain ${format(resourceMulti)}x energy while plane is active.`;
break; break;
case "influences":
const randomInfluence = pickRandom(
(Object.keys(influenceTypes) as Influences[]).reduce((acc, curr) => {
acc[curr] =
curr in main.influenceNodes.value ||
influenceTreasures.includes(curr)
? 0
: 1;
return acc;
}, {} as Record<Influences, number>),
random
);
influenceTreasures.push(randomInfluence);
description = `Gain a new portal influence`;
onComplete = () =>
main.board.placeInAvailableSpace({
id: getUniqueNodeID(main.board),
position: {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
...main.board.types.portal.nodes.value.find(
n => (n.state as unknown as PortalState).id === id
)!.position
},
type: "influence",
state: {
type: randomInfluence,
data: influenceTypes[randomInfluence].initialData
}
});
break;
} }
const milestoneVisibility = visibility; const milestoneVisibility = visibility;
const cost = nextCost.value; const cost = nextCost.value;
@ -510,6 +593,7 @@ export function createPlane(id: string, tier: Resources, seed: number) {
return { return {
tier: persistent(tier), tier: persistent(tier),
seed: persistent(seed), seed: persistent(seed),
influences: persistent(influences as unknown as State[]),
name, name,
color, color,
resource, resource,
@ -539,7 +623,7 @@ export function createPlane(id: string, tier: Resources, seed: number) {
style="display: inline" style="display: inline"
onClick={() => (showModifiersModal.value = true)} onClick={() => (showModifiersModal.value = true)}
> >
open modifiers modifiers
</button> </button>
</span> </span>
</StickyVue> </StickyVue>
@ -578,8 +662,23 @@ export function createPlane(id: string, tier: Resources, seed: number) {
} }
// Using separate method from what's used in mining, because planes are influenced by influences and not things like dowsing // Using separate method from what's used in mining, because planes are influenced by influences and not things like dowsing
function getRandomResource(random: () => number) { function getRandomResource(random: () => number, influences: InfluenceState[]) {
const sumResourceWeights = (Object.values(mineLootTable) as number[]).reduce((a, b) => a + b); influences = influences.filter(
i => i.type === "increaseResources" || i.type === "decreaseResources"
);
const sumResourceWeights = (Object.keys(mineLootTable) as Resources[]).reduce((a, b) => {
let weight = mineLootTable[b];
influences
.filter(i => i.data === b)
.forEach(influence => {
if (influence.type === "increaseResources") {
weight *= 1000;
} else {
weight /= 1000;
}
});
return a + weight;
}, 0);
const resourceWeightsKeys = Object.keys(mineLootTable) as Resources[]; const resourceWeightsKeys = Object.keys(mineLootTable) as Resources[];
const r = Math.floor(random() * sumResourceWeights); const r = Math.floor(random() * sumResourceWeights);
let weight = 0; let weight = 0;

View file

@ -66,7 +66,7 @@ export interface EmpowererState {
export interface PortalGeneratorState { export interface PortalGeneratorState {
tier: Resources | undefined; tier: Resources | undefined;
influences: string[]; influences: Influences[];
} }
export interface PortalState { export interface PortalState {
@ -74,6 +74,11 @@ export interface PortalState {
powered: boolean; powered: boolean;
} }
export interface InfluenceState {
type: Influences;
data: State;
}
export const mineLootTable = { export const mineLootTable = {
dirt: 120, dirt: 120,
sand: 60, sand: 60,
@ -216,6 +221,95 @@ const passives = {
export type Passives = keyof typeof passives; export type Passives = keyof typeof passives;
export const influences = {
increaseResources: {
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: {
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: {
description: "Increase length",
cost: 100,
initialData: undefined
},
increaseCaches: {
description: "Increase caches odds",
cost: 10,
initialData: undefined
},
increaseGens: {
description: "Increase generators odds",
cost: 10,
initialData: undefined
},
increaseInfluences: {
description: "Increase influences odds",
cost: 10,
initialData: undefined
},
increaseEnergyMults: {
description: "Increase energy mults odds",
cost: 10,
initialData: undefined
},
increaseResourceMults: {
description: "Increase resource mults odds",
cost: 10,
initialData: undefined
},
increaseDiff: {
description: "Increase difficulty/rewards odds",
cost: 10,
initialData: undefined
},
decreaseDiff: {
description: "Decrease difficulty/rewards odds",
cost: 10,
initialData: undefined
},
increaseRewards: {
description: "Increase rewards level",
cost: 1e4,
initialData: undefined
}
// relic: {
// description: "Max length/difficulty, add tier-unique relic",
// cost: 1e6,
// initialData: undefined
// }
} as const satisfies Record<
string,
{
description: string | ((state: InfluenceState) => string);
cost: DecimalSource;
initialData?: State;
}
>;
export type Influences = keyof typeof influences;
/** /**
* @hidden * @hidden
*/ */
@ -242,6 +336,13 @@ export const main = createLayer("main", function (this: BaseLayer) {
iron: board.types.portalGenerator.nodes.value[0] iron: board.types.portalGenerator.nodes.value[0]
})); }));
const influenceNodes: ComputedRef<Record<Influences, BoardNode>> = computed(() => ({
...board.types.influence.nodes.value.reduce((acc, curr) => {
acc[(curr.state as unknown as InfluenceState).type] = curr;
return acc;
}, {} as Record<Influences, BoardNode>)
}));
const resourceLevels = computed(() => const resourceLevels = computed(() =>
resourceNames.reduce((acc, curr) => { resourceNames.reduce((acc, curr) => {
const amount = const amount =
@ -336,11 +437,12 @@ export const main = createLayer("main", function (this: BaseLayer) {
id: "deselect", id: "deselect",
icon: "close", icon: "close",
tooltip: (node: BoardNode) => ({ tooltip: (node: BoardNode) => ({
text: text: "tools" in (node.state as object) ? "Disconnect tools" : "Disconnect resources"
"resources" in (node.state as object) ? "Disconnect resources" : "Disconnect tools"
}), }),
onClick(node: BoardNode) { onClick(node: BoardNode) {
if ("resources" in (node.state as object)) { if (Array.isArray(node.state)) {
node.state = [];
} else if ("resources" in (node.state as object)) {
node.state = { ...(node.state as object), resources: [] }; node.state = { ...(node.state as object), resources: [] };
} else if ("tools" in (node.state as object)) { } else if ("tools" in (node.state as object)) {
node.state = { ...(node.state as object), tools: [] }; node.state = { ...(node.state as object), tools: [] };
@ -349,6 +451,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
board.selectedNode.value = null; board.selectedNode.value = null;
}, },
visibility: (node: BoardNode) => { visibility: (node: BoardNode) => {
if (Array.isArray(node.state)) {
return node.state.length > 0;
}
if ("resources" in (node.state as object)) { if ("resources" in (node.state as object)) {
return (node.state as { resources: Resources[] }).resources.length > 0; return (node.state as { resources: Resources[] }).resources.length > 0;
} }
@ -886,11 +991,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
}-tier portal` }-tier portal`
}; };
} }
if ((board as GenericBoard).draggingNode.value?.type === "resource") { const draggingNode = (board as GenericBoard).draggingNode.value;
const resource = ( if (draggingNode?.type === "resource") {
(board as GenericBoard).draggingNode.value const resource = (draggingNode.state as unknown as ResourceState).type;
?.state as unknown as ResourceState
).type;
const text = const text =
(node.state as unknown as PortalGeneratorState).tier === resource (node.state as unknown as PortalGeneratorState).tier === resource
? "Disconnect" ? "Disconnect"
@ -899,8 +1002,17 @@ export const main = createLayer("main", function (this: BaseLayer) {
text, text,
color: "var(--accent2)" 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)"
};
} }
// TODO handle influences
return null; return null;
}, },
actionDistance: Math.PI / 4, actionDistance: Math.PI / 4,
@ -937,12 +1049,18 @@ export const main = createLayer("main", function (this: BaseLayer) {
while (`portal-${id}` in layers) { while (`portal-${id}` in layers) {
id++; id++;
} }
const { tier, influences } =
node.state as unknown as PortalGeneratorState;
addLayer( addLayer(
createPlane( createPlane(
`portal-${id}`, `portal-${id}`,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion tier!,
(node.state as unknown as PortalGeneratorState).tier!, Math.floor(Math.random() * 4294967296),
Math.floor(Math.random() * 4294967296) influences.map(
influence =>
influenceNodes.value[influence]
.state as unknown as InfluenceState
)
), ),
player player
); );
@ -974,7 +1092,8 @@ export const main = createLayer("main", function (this: BaseLayer) {
tier: droppedType === currentType ? undefined : droppedType tier: droppedType === currentType ? undefined : droppedType
}; };
} else if (otherNode.type === "influence") { } else if (otherNode.type === "influence") {
const droppedInfluence = otherNode.state as string; const droppedInfluence = (otherNode.state as unknown as InfluenceState)
.type;
const currentInfluences = (node.state as unknown as PortalGeneratorState) const currentInfluences = (node.state as unknown as PortalGeneratorState)
.influences; .influences;
if (currentInfluences.includes(droppedInfluence)) { if (currentInfluences.includes(droppedInfluence)) {
@ -1022,6 +1141,42 @@ export const main = createLayer("main", function (this: BaseLayer) {
outlineColor: node => outlineColor: node =>
(layers[(node.state as unknown as PortalState).id] as GenericPlane).background, (layers[(node.state as unknown as PortalState).id] as GenericPlane).background,
draggable: true draggable: true
},
influence: {
shape: Shape.Circle,
size: 50,
title: node => (node.state as unknown as InfluenceState).type,
label: node => {
if (node === 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 };
}
return null;
},
actionDistance: Math.PI / 4,
actions: [deselectAllAction],
canAccept: (node, otherNode) => {
if (otherNode.type !== "resource") {
return false;
}
return Array.isArray(node.state);
},
onDrop: (node, otherNode) => {
if (otherNode.type !== "resource") {
return;
}
const resource = (otherNode.state as unknown as ResourceState).type;
const resources = node.state as Resources[];
if (resources.includes(resource)) {
node.state = resources.filter(r => r !== resource);
} else {
node.state = [...resources, resource];
}
board.selectedNode.value = node;
},
outlineColor: "var(--danger)",
draggable: true
} }
}, },
style: { style: {
@ -1098,21 +1253,25 @@ export const main = createLayer("main", function (this: BaseLayer) {
}); });
} }
if (portalGenerator.value != null) { if (portalGenerator.value != null) {
if ((portalGenerator.value.state as unknown as PortalGeneratorState).tier != null) { const state = portalGenerator.value.state as unknown as PortalGeneratorState;
if (state.tier != null) {
links.push({ links.push({
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion startNode: portalGenerator.value,
startNode: portalGenerator.value!, endNode: resourceNodes.value[state.tier],
endNode:
resourceNodes.value[
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
(portalGenerator.value.state as unknown as PortalGeneratorState)
.tier!
],
stroke: "var(--foreground)", stroke: "var(--foreground)",
strokeWidth: 4 strokeWidth: 4
}); });
// TODO link to influences
} }
state.influences.forEach(influence => {
console.log(influence);
links.push({
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
startNode: portalGenerator.value!,
endNode: influenceNodes.value[influence],
stroke: "var(--foreground)",
strokeWidth: 4
});
});
(board as GenericBoard).types.portal.nodes.value.forEach(node => { (board as GenericBoard).types.portal.nodes.value.forEach(node => {
const plane = layers[(node.state as unknown as PortalState).id] as GenericPlane; const plane = layers[(node.state as unknown as PortalState).id] as GenericPlane;
plane.links.value.forEach(n => { plane.links.value.forEach(n => {
@ -1142,6 +1301,19 @@ export const main = createLayer("main", function (this: BaseLayer) {
return links; return links;
}); });
} }
Object.values(influenceNodes.value).forEach(node => {
const state = node.state as unknown as InfluenceState;
if (state.type === "increaseResources" || state.type === "decreaseResources") {
(state.data as Resources[]).forEach(resource => {
links.push({
startNode: node,
endNode: resourceNodes.value[resource],
stroke: "var(--foreground)",
strokeWidth: 4
});
});
}
});
return links; return links;
} }
})); }));
@ -1597,6 +1769,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
passives, passives,
resourceNodes, resourceNodes,
toolNodes, toolNodes,
influenceNodes,
grantResource, grantResource,
activePortals, activePortals,
display: jsx(() => ( display: jsx(() => (
@ -1630,7 +1803,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
style="display: inline" style="display: inline"
onClick={() => (showModifiersModal.value = true)} onClick={() => (showModifiersModal.value = true)}
> >
open modifiers modifiers
</button> </button>
</span> </span>
{player.devSpeed === 0 ? ( {player.devSpeed === 0 ? (
@ -1662,7 +1835,8 @@ export const getInitialLayers = (
createPlane( createPlane(
`portal-${id}`, `portal-${id}`,
layer.tier ?? "dirt", layer.tier ?? "dirt",
layer.seed ?? Math.floor(Math.random() * 4294967296) layer.seed ?? Math.floor(Math.random() * 4294967296),
(layer.influences ?? []) as unknown as InfluenceState[]
) )
); );
id++; id++;