Merge branch 'day-20-factory' of https://github.com/thepaperpilot/Advent-Incremental into day-20-factory

This commit is contained in:
circle-gon 2022-12-21 14:15:43 +00:00
commit 6f3f1f1985
2 changed files with 208 additions and 122 deletions

View file

@ -10,6 +10,7 @@ import { globalBus } from "game/events";
import { createLayer } from "game/layers"; import { createLayer } from "game/layers";
import { Persistent, persistent, State } from "game/persistence"; import { Persistent, persistent, State } from "game/persistence";
import player from "game/player"; import player from "game/player";
import { format, formatWhole } from "util/bignum";
import { Direction } from "util/common"; import { Direction } from "util/common";
import { computed, ComputedRef, reactive, ref, watchEffect } from "vue"; import { computed, ComputedRef, reactive, ref, watchEffect } from "vue";
import conveyor from "./factory-components/conveyor.png"; import conveyor from "./factory-components/conveyor.png";
@ -103,31 +104,19 @@ const factory = createLayer(id, () => {
imageSrc: cursor, imageSrc: cursor,
name: "Cursor", name: "Cursor",
description: "Use this to move.", description: "Use this to move.",
tick: 0, tick: 0
consumption: {},
consumptionStock: {},
production: {},
productionStock: {}
}, },
rotate: { rotate: {
imageSrc: rotate, imageSrc: rotate,
name: "Rotate", name: "Rotate",
description: "Use this to rotate components.", description: "Use this to rotate components.",
tick: 0, tick: 0
consumption: {},
consumptionStock: {},
production: {},
productionStock: {}
}, },
conveyor: { conveyor: {
imageSrc: conveyor, imageSrc: conveyor,
name: "Conveyor", name: "Conveyor",
description: "Moves 1 item per tick.", description: "Moves 1 item per tick.",
tick: 1, tick: 1,
consumption: {},
consumptionStock: {},
production: {},
productionStock: {},
ports: { ports: {
[Direction.Left]: { [Direction.Left]: {
type: "input" type: "input"
@ -139,24 +128,51 @@ const factory = createLayer(id, () => {
}, },
square: { square: {
imageSrc: square, imageSrc: square,
name: "???", name: "Producer",
description: "Produces 1 square every 1 tick.", description: "Produces 1 square every 1 tick.",
tick: 1, tick: 1,
production: { outputs: {
square: 1 square: {
}, amount: 1
productionStock: {
square: Infinity
},
consumption: {},
consumptionStock: {}
} }
} as Record<string, FactoryComponentDeclaration>; }
},
receiver: {
imageSrc: square,
name: "Receiver",
description: "Obtains squares. Pretty much does nothing else.",
tick: 0,
inputs: {
square: {
amount: Infinity
}
}
},
shrinker: {
imageSrc: square,
name: "Shrinker",
description:
"Converts 100 squares to 1 square. I don't know why you would want to do this but here you go anyways.",
tick: 1,
inputs: {
square: {
amount: 100,
capacity: 10000
}
},
outputs: {
square: {
amount: 1,
capacity: 10
}
}
}
} as Record<FactoryCompNames, FactoryComponentDeclaration>;
const RESOURCES = { const RESOURCES = {
square: square square: square
} as Record<string, string>; } as Record<string, string>;
type FactoryCompNames = keyof typeof FACTORY_COMPONENTS; type FactoryCompNames = "cursor" | "rotate" | "conveyor" | "square" | "receiver" | "shrinker";
type BuildableCompName = Exclude<FactoryCompNames, "cursor">; type BuildableCompName = Exclude<FactoryCompNames, "cursor">;
interface FactoryComponentBase extends Record<string, State> { interface FactoryComponentBase extends Record<string, State> {
@ -165,10 +181,10 @@ const factory = createLayer(id, () => {
interface FactoryComponentProducer extends FactoryComponentBase { interface FactoryComponentProducer extends FactoryComponentBase {
type: Exclude<BuildableCompName, "conveyor">; type: Exclude<BuildableCompName, "conveyor">;
consumptionStock: Record<string, number>; inputStock?: Record<string, number>;
// current production stock // current production stock
productionStock: Record<string, number>; outputStock?: Record<string, number>;
ticksDone: number; ticksDone: number;
} }
@ -186,13 +202,21 @@ const factory = createLayer(id, () => {
description: string; description: string;
/** amount it consumes */ /** amount it consumes */
consumption: Record<string, number>; inputs?: Record<
/** maximum stock of consumption */ string,
consumptionStock: Record<string, number>; {
amount: number;
capacity?: number;
}
>;
/** amount it produces */ /** amount it produces */
production: Record<string, number>; outputs?: Record<
/** maximum stock of production */ string,
productionStock: Record<string, number>; {
amount: number;
capacity?: number;
}
>;
/** on produce, do something */ /** on produce, do something */
onProduce?: (times: number) => void; onProduce?: (times: number) => void;
@ -223,8 +247,8 @@ const factory = createLayer(id, () => {
// in block amts, not screen // in block amts, not screen
x: number; x: number;
y: number; y: number;
lastX: number; // make blocks turn in random amounts;
lastY: number; turbulance: number;
} }
// mouse positions // mouse positions
@ -321,8 +345,6 @@ const factory = createLayer(id, () => {
globalBus.on("update", diff => { globalBus.on("update", diff => {
if (taskIsRunning || !loaded) return; if (taskIsRunning || !loaded) return;
//debugger //debugger
// will change soon:tm:
const tick = diff;
// make them produce // make them produce
for (const id in components.value) { for (const id in components.value) {
const [x, y] = id.split("x").map(p => +p); const [x, y] = id.split("x").map(p => +p);
@ -339,12 +361,13 @@ const factory = createLayer(id, () => {
// use a copy // use a copy
compData.packages = compData.packages.concat(compData.nextPackages); compData.packages = compData.packages.concat(compData.nextPackages);
compData.nextPackages = []; compData.nextPackages = [];
for (const [key, block] of [...compData.packages].entries()) { for (let key = 0; key < compData.packages.length; key++) {
const inputDirection = rotateDir(data.direction, Direction.Left); const block = compData.packages[key];
const inputDirection = data.direction;
const dirType = getDirection(inputDirection); const dirType = getDirection(inputDirection);
const dirAmt = directionToNum(inputDirection); const dirAmt = directionToNum(inputDirection);
if (dirType === "h") { if (dirType === "h") {
if (block.x <= block.lastX + dirAmt) { if ((block.x - x) * dirAmt >= 1 + block.turbulance) {
const compBehind = compInternalData[x + dirAmt + "x" + y]; const compBehind = compInternalData[x + dirAmt + "x" + y];
const storedComp = components.value[x + dirAmt + "x" + y]; const storedComp = components.value[x + dirAmt + "x" + y];
@ -352,29 +375,34 @@ const factory = createLayer(id, () => {
if (compBehind === undefined) { if (compBehind === undefined) {
// just delete it // just delete it
movingBlocks.removeChild(block.sprite); movingBlocks.removeChild(block.sprite);
compData.packages.splice(key, 1);
} else if (compBehind.type === "conveyor") { } else if (compBehind.type === "conveyor") {
// push it to the next conveyor, kill it from the // push it to the next conveyor, kill it from the
// curent conveyor // curent conveyor
block.lastX += dirAmt; block.turbulance = Math.random() * 0.4 - 0.2;
(compBehind as FactoryInternalConveyor).nextPackages.push(block); (compBehind as FactoryInternalConveyor).nextPackages.push(block);
compData.packages.splice(key, 1);
} else { } else {
// send it to the factory // send it to the factory
// destory its sprite and data // destory its sprite and data
const factoryData = storedComp as FactoryComponentProducer; const factoryData = storedComp as FactoryComponentProducer;
factoryData.consumptionStock[block.type]++; if (factoryData.inputStock !== undefined)
factoryData.inputStock[block.type] = Math.min(
(factoryData.inputStock[block.type] ?? 0) + 1,
FACTORY_COMPONENTS[compBehind.type].inputs?.[block.type]
?.capacity ?? Infinity
);
movingBlocks.removeChild(block.sprite); movingBlocks.removeChild(block.sprite);
compData.packages.splice(key, 1);
} }
compData.packages.splice(key, 1);
key--;
} else { } else {
const change = const change =
dirAmt * Math.min(Math.abs(block.x + 1 - block.lastX), tick); dirAmt * Math.min(Math.abs(x + 1.3 * dirAmt - block.x), diff);
block.x += change; block.x += change;
block.sprite.x += change * blockSize; block.sprite.x += change * blockSize;
} }
} else { } else {
if (block.y <= block.lastY + dirAmt) { if ((block.y - y) * dirAmt >= 1 + block.turbulance) {
const compBehind = compInternalData[x + "x" + (y + dirAmt)]; const compBehind = compInternalData[x + "x" + (y + dirAmt)];
const storedComp = components.value[x + "x" + (y + dirAmt)]; const storedComp = components.value[x + "x" + (y + dirAmt)];
@ -382,23 +410,28 @@ const factory = createLayer(id, () => {
if (compBehind === undefined) { if (compBehind === undefined) {
// just delete it // just delete it
movingBlocks.removeChild(block.sprite); movingBlocks.removeChild(block.sprite);
compData.packages.splice(key, 1);
} else if (compBehind.type === "conveyor") { } else if (compBehind.type === "conveyor") {
// push it to the next conveyor, kill it from the // push it to the next conveyor, kill it from the
// curent conveyor // curent conveyor
block.lastY += dirAmt; block.turbulance = Math.random() * 0.4 - 0.2;
(compBehind as FactoryInternalConveyor).nextPackages.push(block); (compBehind as FactoryInternalConveyor).nextPackages.push(block);
compData.packages.splice(key, 1);
} else { } else {
// send it to the factory // send it to the factory
// destory its sprite and data // destory its sprite and data
const factoryData = storedComp as FactoryComponentProducer; const data = storedComp as FactoryComponentProducer;
factoryData.consumptionStock[block.type]++; if (factoryData.inputs?.[block.type] !== undefined) {
movingBlocks.removeChild(block.sprite); if (data.inputStock === undefined) data.inputStock = {};
compData.packages.splice(key, 1); data.inputStock[block.type] =
(data.inputStock[block.type] ?? 0) + 1;
} }
movingBlocks.removeChild(block.sprite);
}
compData.packages.splice(key, 1);
key--;
} else { } else {
const change = dirAmt * Math.min(Math.abs(block.y - block.lastY), tick); const change =
dirAmt * Math.min(Math.abs(y + 1.3 * dirAmt - block.y), diff);
block.y += change; block.y += change;
block.sprite.y += change * blockSize; block.sprite.y += change * blockSize;
} }
@ -410,57 +443,69 @@ const factory = createLayer(id, () => {
// factory part // factory part
// PRODUCTION // PRODUCTION
if (data.ticksDone >= factoryData.tick) { if (data.ticksDone >= factoryData.tick) {
if (!compData.canProduce.value) continue; if (compData.canProduce.value) {
const cyclesDone = Math.floor(data.ticksDone / factoryData.tick); const cyclesDone = Math.floor(data.ticksDone / factoryData.tick);
factoryData.onProduce?.(cyclesDone); factoryData.onProduce?.(cyclesDone);
for (const [key, val] of Object.entries(factoryData.consumption)) { if (factoryData.inputs !== undefined) {
data.consumptionStock[key] -= val; if (data.inputStock === undefined) data.inputStock = {};
for (const [key, val] of Object.entries(factoryData.inputs)) {
data.inputStock[key] = (data.inputStock[key] ?? 0) - val.amount;
}
}
if (factoryData.outputs !== undefined) {
if (data.outputStock === undefined) data.outputStock = {};
for (const [key, val] of Object.entries(factoryData.outputs)) {
data.outputStock[key] = (data.outputStock[key] ?? 0) + val.amount;
} }
for (const [key, val] of Object.entries(factoryData.production)) {
data.productionStock[key] += val;
} }
data.ticksDone -= cyclesDone * factoryData.tick; data.ticksDone -= cyclesDone * factoryData.tick;
}
} else { } else {
data.ticksDone += tick; data.ticksDone += diff;
} }
// now look at each component direction and see if it accepts items coming in // now look at each component direction and see if it accepts items coming in
// components are 1x1 so simple math for now // components are 1x1 so simple math for now
let yInc = 0; const incs: [number, number][] = [];
let xInc = 0;
if ( if (
components.value[x + "x" + (y + 1)]?.type === "conveyor" && components.value[x + "x" + (y + 1)]?.type === "conveyor" &&
components.value[x + "x" + (y + 1)].direction === Direction.Up components.value[x + "x" + (y + 1)].direction === Direction.Down
) { ) {
yInc = 1; incs.push([0, 1]);
} else if ( }
if (
components.value[x + "x" + (y - 1)]?.type === "conveyor" && components.value[x + "x" + (y - 1)]?.type === "conveyor" &&
components.value[x + "x" + (y - 1)].direction === Direction.Down components.value[x + "x" + (y - 1)].direction === Direction.Up
) { ) {
yInc = -1; incs.push([0, -1]);
} else if ( }
if (
components.value[x + 1 + "x" + y]?.type === "conveyor" && components.value[x + 1 + "x" + y]?.type === "conveyor" &&
components.value[x + 1 + "x" + y].direction === Direction.Right components.value[x + 1 + "x" + y].direction === Direction.Right
) { ) {
xInc = 1; incs.push([1, 0]);
} else if ( }
if (
components.value[x - 1 + "x" + y]?.type === "conveyor" && components.value[x - 1 + "x" + y]?.type === "conveyor" &&
components.value[x - 1 + "x" + y].direction === Direction.Left components.value[x - 1 + "x" + y].direction === Direction.Left
) { ) {
xInc = -1; incs.push([-1, 0]);
} }
// no suitable location to dump stuff in // no suitable location to dump stuff in
// console.log(x, y); // console.log(x, y);
// debugger; // debugger;
if (xInc === 0 && yInc === 0) continue; if (incs.length <= 0) continue;
const [xInc, yInc] = incs[Math.floor(Math.random() * incs.length)];
let itemToMove: [string, number] | undefined = undefined; let itemToMove: [string, number] | undefined = undefined;
for (const [name, amt] of Object.entries(data.productionStock)) { if (data.outputStock !== undefined) {
for (const [name, amt] of Object.entries(data.outputStock)) {
if (amt >= 1) { if (amt >= 1) {
itemToMove = [name, amt]; itemToMove = [name, amt];
data.productionStock[name]--; data.outputStock[name]--;
break; break;
} }
} }
}
// there is nothing to move // there is nothing to move
if (itemToMove === undefined) continue; if (itemToMove === undefined) continue;
const texture = Assets.get(RESOURCES[itemToMove[0]]); const texture = Assets.get(RESOURCES[itemToMove[0]]);
@ -479,20 +524,17 @@ const factory = createLayer(id, () => {
// if X is being moved, then we don't need to adjust x // if X is being moved, then we don't need to adjust x
// however it needs to be aligned if Y is being moved // however it needs to be aligned if Y is being moved
// vice-versa // vice-versa
const spriteDiffX = xInc !== 0 ? 0 : 0.5; sprite.x = (x + xInc * 0.3) * blockSize;
const spriteDiffY = yInc !== 0 ? 0 : 0.5; sprite.y = (y + yInc * 0.3) * blockSize;
sprite.x = (x + spriteDiffX) * blockSize;
sprite.y = (y + spriteDiffY) * blockSize;
sprite.anchor.set(0.5); sprite.anchor.set(0.5);
sprite.width = blockSize / 2.5; sprite.width = blockSize / 2.5;
sprite.height = blockSize / 5; sprite.height = blockSize / 5;
//console.log(sprite); //console.log(sprite);
const block: Block = { const block: Block = {
sprite, sprite,
x: x, x: x + xInc * 0.3,
y: y, y: y + yInc * 0.3,
lastX: x, turbulance: Math.random() * 0.4 - 0.2,
lastY: y,
type: itemToMove[0] type: itemToMove[0]
}; };
@ -533,17 +575,17 @@ const factory = createLayer(id, () => {
components.value[x + "x" + y] = { components.value[x + "x" + y] = {
ticksDone: 0, ticksDone: 0,
direction: Direction.Right, direction: Direction.Right,
consumptionStock: inputStock:
data.type === "conveyor" factoryBaseData.inputs === undefined
? undefined ? undefined
: Object.fromEntries( : Object.fromEntries(
Object.keys(factoryBaseData.consumptionStock).map(i => [i, 0]) Object.entries(factoryBaseData.inputs).map(x => [x[0], 0])
), ),
productionStock: outputStock:
data.type === "conveyor" factoryBaseData.outputs === undefined
? undefined ? undefined
: Object.fromEntries( : Object.fromEntries(
Object.keys(factoryBaseData.productionStock).map(i => [i, 0]) Object.entries(factoryBaseData.outputs).map(x => [x[0], 0])
), ),
...data ...data
} as FactoryComponent; } as FactoryComponent;
@ -557,18 +599,18 @@ const factory = createLayer(id, () => {
if (!(factoryBaseData.canProduce?.value ?? true)) return false; if (!(factoryBaseData.canProduce?.value ?? true)) return false;
// this should NEVER be null // this should NEVER be null
const compData = components.value[x + "x" + y] as FactoryComponentProducer; const compData = components.value[x + "x" + y] as FactoryComponentProducer;
for (const [key, res] of Object.entries(compData.productionStock)) { if (factoryBaseData.inputs !== undefined) {
// if the current stock + production is more than you can handle for (const [res, val] of Object.entries(factoryBaseData.inputs))
if ((compData.inputStock?.[res] ?? 0) < val.amount) return false;
}
if (factoryBaseData.outputs !== undefined) {
for (const [res, val] of Object.entries(factoryBaseData.outputs))
if ( if (
res + factoryBaseData.production[key] > (compData.outputStock?.[res] ?? 0) + val.amount >
factoryBaseData.productionStock[key] (val.capacity ?? Infinity)
) )
return false; return false;
} }
for (const [key, res] of Object.entries(compData.consumptionStock)) {
// make sure you have enough to produce
if (res < factoryBaseData.consumptionStock[key]) return false;
}
return true; return true;
}), }),
sprite sprite
@ -681,7 +723,16 @@ const factory = createLayer(id, () => {
} else if (e.button === 2) { } else if (e.button === 2) {
const data = compInternalData[x + "x" + y]; const data = compInternalData[x + "x" + y];
if (data === undefined) return; if (data === undefined) return;
if (data.type === "conveyor") {
const cData = data as FactoryInternalConveyor;
for (const p of cData.packages) {
p.sprite.destroy();
}
}
delete components.value[x + "x" + y]; delete components.value[x + "x" + y];
delete compInternalData[x + "x" + y];
spriteContainer.removeChild(data.sprite); spriteContainer.removeChild(data.sprite);
} }
} }
@ -746,24 +797,58 @@ const factory = createLayer(id, () => {
<br /> <br />
{compHovered.value.type !== "conveyor" ? ( {compHovered.value.type !== "conveyor" ? (
<> <>
Stock:{" "} {compHovered.value.inputStock !== undefined ? (
{Object.entries({ <>
...(compHovered.value.productionStock as Record< <br />
string, <h5>Inputs:</h5>
number {Object.entries(compHovered.value.inputStock).map(x => (
>), <div>
...(compHovered.value.consumptionStock as Record< {x[0]}: {formatWhole(x[1])}
string, {FACTORY_COMPONENTS[
number compHovered.value?.type ?? "cursor"
>) ].inputs?.[x[0]].amount !== undefined
}).map(i => { ? " / " +
return `${i[0]}: ${i[1]}/${ formatWhole(
FACTORY_COMPONENTS[compHovered.value?.type ?? "cursor"] FACTORY_COMPONENTS[
.consumptionStock[i[0]] ?? compHovered.value?.type ?? "cursor"
FACTORY_COMPONENTS[compHovered.value?.type ?? "cursor"] ].inputs?.[x[0]].amount ?? 0
.productionStock[i[0]] )
}`; : ""}
})} {FACTORY_COMPONENTS[
compHovered.value?.type ?? "cursor"
].inputs?.[x[0]].capacity !== undefined
? " / " +
formatWhole(
FACTORY_COMPONENTS[
compHovered.value?.type ?? "cursor"
].inputs?.[x[0]].capacity ?? 0
)
: ""}
</div>
))}
</>
) : undefined}
{compHovered.value.outputStock !== undefined ? (
<>
<br />
<h5>Outputs:</h5>
{Object.entries(compHovered.value.outputStock).map(x => (
<div>
{x[0]}: {formatWhole(x[1])}
{FACTORY_COMPONENTS[
compHovered.value?.type ?? "cursor"
].outputs?.[x[0]].capacity !== undefined
? " / " +
formatWhole(
FACTORY_COMPONENTS[
compHovered.value?.type ?? "cursor"
].outputs?.[x[0]].capacity ?? 0
)
: ""}
</div>
))}
</>
) : undefined}
</> </>
) : undefined} ) : undefined}
</div> </div>

View file

@ -48,6 +48,7 @@ export type State =
| string | string
| number | number
| boolean | boolean
| undefined
| DecimalSource | DecimalSource
| { [key: string]: State } | { [key: string]: State }
| { [key: number]: State }; | { [key: number]: State };