Working animations
This commit is contained in:
parent
aa0e84de69
commit
688f548ef3
|
|
@ -15,7 +15,7 @@ export default class Graphics {
|
||||||
constructor(private canvas: HTMLCanvasElement) {
|
constructor(private canvas: HTMLCanvasElement) {
|
||||||
this.context = canvas.getContext('2d')!;
|
this.context = canvas.getContext('2d')!;
|
||||||
this.context.imageSmoothingEnabled = false;
|
this.context.imageSmoothingEnabled = false;
|
||||||
this.context.font = 'bold 0.4px sans-serif';
|
this.context.font = 'bold 0.3px sans-serif';
|
||||||
this.context.textRendering = 'optimizeSpeed';
|
this.context.textRendering = 'optimizeSpeed';
|
||||||
this.context.textAlign = 'center';
|
this.context.textAlign = 'center';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { type Tile, TileType, getPortDirections, PortDirection, LIMITS, type Resource } from "./world";
|
import { type Tile, TileType, getPortDirections, PortDirection, LIMITS, type Resource, getTileOutput } from "./world";
|
||||||
|
|
||||||
import { ALL_DIRECTIONS, Direction, makeImage, movePoint } from "./utils";
|
import { ALL_DIRECTIONS, Direction, makeImage, movePoint } from "./utils";
|
||||||
|
|
||||||
|
|
@ -21,29 +21,31 @@ const notImage = makeImage(notSrc);
|
||||||
const andImage = makeImage(andSrc);
|
const andImage = makeImage(andSrc);
|
||||||
const orImage = makeImage(orSrc);
|
const orImage = makeImage(orSrc);
|
||||||
|
|
||||||
|
const px = 1 / 32;
|
||||||
export const renderResource = (ctx: CanvasRenderingContext2D, tile: Tile, resource?: Resource) => {
|
export const renderResource = (ctx: CanvasRenderingContext2D, tile: Tile, resource?: Resource) => {
|
||||||
const resources: [Direction, Resource | undefined][] = resource
|
let resources: [Direction, Resource | undefined][] = resource
|
||||||
? [[Direction.NONE, resource]]
|
? [[Direction.NONE, resource]]
|
||||||
: [...ALL_DIRECTIONS, Direction.NONE].map((d) => [d, tile.inv[d]]);
|
: [...ALL_DIRECTIONS, Direction.NONE].map((d) => [d, tile.inv[d]]);
|
||||||
|
|
||||||
const oldStyle = ctx.fillStyle;
|
const [output] = getTileOutput(tile);
|
||||||
const px = 1 / 32;
|
if (output && (tile.timer ?? 0) <= 0) {
|
||||||
|
resources = [[Direction.NONE, output]];
|
||||||
|
}
|
||||||
|
|
||||||
let wasOtherDrawn = false;
|
let wasOtherDrawn = false;
|
||||||
for (const [direction, res] of resources) {
|
for (const [direction, res] of resources) {
|
||||||
if (direction === Direction.NONE && wasOtherDrawn) continue;
|
if (direction === Direction.NONE && wasOtherDrawn) continue;
|
||||||
if (res) {
|
if (res) {
|
||||||
const str = res.toString(2);
|
const str = res.value.toString(2);
|
||||||
const amount = (tile.animationTimer ?? 0) / LIMITS[tile.type].cooldown;
|
const [timer, timerMax] = tile.inputAnimations?.get(direction) ?? [0, 1];
|
||||||
|
const amount = timer / timerMax;
|
||||||
|
|
||||||
const [x, y] = movePoint([0.5, 0.65], direction, amount);
|
const [x, y] = movePoint([0.5, 0.62], direction, amount);
|
||||||
|
|
||||||
ctx.fillStyle = 'white';
|
ctx.strokeStyle = 'white';
|
||||||
ctx.fillText(str, x - px, y - px);
|
ctx.lineWidth = px * 2;
|
||||||
ctx.fillText(str, x - px, y + px);
|
ctx.miterLimit = 2;
|
||||||
ctx.fillText(str, x + px, y - px);
|
ctx.strokeText(str, x, y);
|
||||||
ctx.fillText(str, x + px, y + px);
|
|
||||||
ctx.fillStyle = oldStyle;
|
|
||||||
ctx.fillText(str, x, y);
|
ctx.fillText(str, x, y);
|
||||||
wasOtherDrawn = true;
|
wasOtherDrawn = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,16 +23,17 @@ interface TileLimit {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LIMITS: Record<TileType, TileLimit> = {
|
export const LIMITS: Record<TileType, TileLimit> = {
|
||||||
|
[TileType.CONVEYOR]: { cooldown: 300, resourcesRequired: 1, capacity: 1, maxOutputs: 4 },
|
||||||
|
|
||||||
[TileType.DESTINATION]: { cooldown: 0, resourcesRequired: 1, capacity: 4, maxOutputs: 0 },
|
[TileType.DESTINATION]: { cooldown: 0, resourcesRequired: 1, capacity: 4, maxOutputs: 0 },
|
||||||
[TileType.SOURCE]: { cooldown: 10000, resourcesRequired: 0, capacity: 0, maxOutputs: 0 },
|
[TileType.SOURCE]: { cooldown: 10000, resourcesRequired: 0, capacity: 0, maxOutputs: 0 },
|
||||||
[TileType.EXTRACTOR]: { cooldown: 5000, resourcesRequired: 0, capacity: 0, maxOutputs: 4 },
|
[TileType.EXTRACTOR]: { cooldown: 600, resourcesRequired: 0, capacity: 0, maxOutputs: 4 },
|
||||||
[TileType.CONVEYOR]: { cooldown: 3000, resourcesRequired: 1, capacity: 1, maxOutputs: 4 },
|
|
||||||
[TileType.NOT]: { cooldown: 3000, resourcesRequired: 1, capacity: 1, maxOutputs: 1 },
|
[TileType.NOT]: { cooldown: 3000, resourcesRequired: 1, capacity: 1, maxOutputs: 1 },
|
||||||
[TileType.AND]: { cooldown: 3000, resourcesRequired: 2, capacity: 2, maxOutputs: 1 },
|
[TileType.AND]: { cooldown: 6000, resourcesRequired: 2, capacity: 2, maxOutputs: 1 },
|
||||||
[TileType.OR]: { cooldown: 3000, resourcesRequired: 2, capacity: 2, maxOutputs: 1 },
|
[TileType.OR]: { cooldown: 3000, resourcesRequired: 2, capacity: 2, maxOutputs: 1 },
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Resource = number;
|
export type Resource = { readonly value: number };
|
||||||
|
|
||||||
interface Port {
|
interface Port {
|
||||||
direction: PortDirection;
|
direction: PortDirection;
|
||||||
|
|
@ -40,16 +41,17 @@ interface Port {
|
||||||
|
|
||||||
type Ports = Partial<Record<Direction, Port>>;
|
type Ports = Partial<Record<Direction, Port>>;
|
||||||
type Inventory = Partial<Record<Direction, Resource>>;
|
type Inventory = Partial<Record<Direction, Resource>>;
|
||||||
|
type AnimationTimers = Map<Direction, [number, number]>;
|
||||||
|
|
||||||
interface BaseTile {
|
interface BaseTile {
|
||||||
ports: Ports;
|
ports: Ports;
|
||||||
inv: Inventory;
|
inv: Inventory;
|
||||||
|
inputAnimations?: AnimationTimers;
|
||||||
|
|
||||||
bufferedDirections?: Direction[];
|
bufferedDirections?: Direction[];
|
||||||
nextInput?: Direction;
|
nextInput?: Direction;
|
||||||
nextOutput?: Direction;
|
nextOutput?: Direction;
|
||||||
timer?: number;
|
timer?: number;
|
||||||
animationTimer?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TileDestination extends BaseTile {
|
interface TileDestination extends BaseTile {
|
||||||
|
|
@ -105,7 +107,7 @@ export const getTileOutput = (tile: Tile): [Resource | undefined, Direction[]] =
|
||||||
if (inputDirection) {
|
if (inputDirection) {
|
||||||
let resource = tile.inv[inputDirection];
|
let resource = tile.inv[inputDirection];
|
||||||
if (tile.type === TileType.NOT && resource) {
|
if (tile.type === TileType.NOT && resource) {
|
||||||
resource = ~(resource) & 0xF;
|
resource = { value: ~(resource.value) & 0xF };
|
||||||
}
|
}
|
||||||
inputDirections = [inputDirection];
|
inputDirections = [inputDirection];
|
||||||
bufferedResource = resource;
|
bufferedResource = resource;
|
||||||
|
|
@ -115,10 +117,10 @@ export const getTileOutput = (tile: Tile): [Resource | undefined, Direction[]] =
|
||||||
const [x, y] = inputDirections.map(d => tile.inv[d]!);
|
const [x, y] = inputDirections.map(d => tile.inv[d]!);
|
||||||
switch (tile.type) {
|
switch (tile.type) {
|
||||||
case TileType.AND:
|
case TileType.AND:
|
||||||
bufferedResource = (x & y) & 0xF;
|
bufferedResource = { value: (x.value & y.value) & 0xF };
|
||||||
break;
|
break;
|
||||||
case TileType.OR:
|
case TileType.OR:
|
||||||
bufferedResource = (x | y) & 0xF;
|
bufferedResource = { value: (x.value | y.value) & 0xF };
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -296,7 +298,7 @@ export default class World {
|
||||||
const tile = this.world.get(pid);
|
const tile = this.world.get(pid);
|
||||||
if (tile) return tile;
|
if (tile) return tile;
|
||||||
|
|
||||||
if (Math.abs(x) >= 5 && Math.abs(y) >= 5) {
|
if (Math.abs(x) >= 3 && Math.abs(y) >= 3) {
|
||||||
return this.genTile(trunc(position));
|
return this.genTile(trunc(position));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -307,8 +309,8 @@ export default class World {
|
||||||
const hash = cyrb32(this.seed, ...position);
|
const hash = cyrb32(this.seed, ...position);
|
||||||
|
|
||||||
if ((hash & 0xFF) === 42) {
|
if ((hash & 0xFF) === 42) {
|
||||||
const resource = (hash >> 12) & 0xF || 1;
|
const value = (hash >> 12) & 0xF || 1;
|
||||||
const newTile: Tile = { type: TileType.SOURCE, resource, ports: {}, inv: {} };
|
const newTile: Tile = { type: TileType.SOURCE, resource: { value }, ports: {}, inv: {} };
|
||||||
return newTile;
|
return newTile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -339,12 +341,13 @@ export default class World {
|
||||||
tile.nextOutput = findNextPort(tile.ports, PortDirection.OUTPUT, tile.nextOutput);
|
tile.nextOutput = findNextPort(tile.ports, PortDirection.OUTPUT, tile.nextOutput);
|
||||||
}
|
}
|
||||||
const position = deid(pid);
|
const position = deid(pid);
|
||||||
if (tile.timer && tile.timer > 0) {
|
tile.timer = Math.max(0, (tile.timer ?? 0) - dt);
|
||||||
tile.timer -= dt;
|
|
||||||
}
|
const animationTimers: AnimationTimers = tile.inputAnimations ?? new Map<Direction, [number, number]>();
|
||||||
if (tile.animationTimer && tile.animationTimer > 0) {
|
for (const [direction, [timer, timerMax]] of animationTimers) {
|
||||||
tile.animationTimer -= dt;
|
animationTimers.set(direction, [Math.max(0, timer - dt), timerMax]);
|
||||||
}
|
}
|
||||||
|
tile.inputAnimations = animationTimers;
|
||||||
|
|
||||||
if (!tile.timer || tile.timer <= 0) {
|
if (!tile.timer || tile.timer <= 0) {
|
||||||
switch (tile.type) {
|
switch (tile.type) {
|
||||||
|
|
@ -362,28 +365,32 @@ export default class World {
|
||||||
const [neighbour, inputDirection, neighbourPosition] = this.getNeighbour(position, tile.nextOutput);
|
const [neighbour, inputDirection, neighbourPosition] = this.getNeighbour(position, tile.nextOutput);
|
||||||
if (neighbour) {
|
if (neighbour) {
|
||||||
const [priorityPusher,] = this.getNeighbour(neighbourPosition, neighbour.nextInput);
|
const [priorityPusher,] = this.getNeighbour(neighbourPosition, neighbour.nextInput);
|
||||||
const limits = LIMITS[neighbour.type];
|
const neighbourLimits = LIMITS[neighbour.type];
|
||||||
|
const limits = LIMITS[tile.type];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
neighbour.ports[inputDirection]?.direction === PortDirection.INPUT
|
neighbour.ports[inputDirection]?.direction === PortDirection.INPUT
|
||||||
&& neighbour.inv[inputDirection] == null
|
&& neighbour.inv[inputDirection] == null
|
||||||
&& getTileInputs(neighbour).length < limits.capacity
|
&& getTileInputs(neighbour).length < neighbourLimits.capacity
|
||||||
&& (
|
&& (
|
||||||
neighbour.nextInput == null
|
neighbour.nextInput == null
|
||||||
|| neighbour.nextInput === inputDirection
|
|| neighbour.nextInput === inputDirection
|
||||||
|| !priorityPusher
|
|| !priorityPusher
|
||||||
|| getTileOutput(priorityPusher)[0] == null
|
|| getTileOutput(priorityPusher)[0] == null
|
||||||
|| limits.resourcesRequired > 1
|
|| neighbourLimits.resourcesRequired > 1
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
neighbour.inv[inputDirection] = resource;
|
neighbour.inv[inputDirection] = resource;
|
||||||
if (getTileInputs(neighbour).length >= limits.capacity) {
|
neighbour.inputAnimations =
|
||||||
neighbour.timer = limits.cooldown;
|
(neighbour.inputAnimations ?? new Map<Direction, [number, number]>())
|
||||||
|
.set(inputDirection, [LIMITS[TileType.CONVEYOR].cooldown, LIMITS[TileType.CONVEYOR].cooldown]);
|
||||||
|
if (getTileInputs(neighbour).length >= neighbourLimits.capacity) {
|
||||||
|
neighbour.timer = neighbourLimits.cooldown;
|
||||||
neighbour.nextInput = findNextPort(neighbour.ports, PortDirection.INPUT, neighbour.nextInput);
|
neighbour.nextInput = findNextPort(neighbour.ports, PortDirection.INPUT, neighbour.nextInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
inputDirections.forEach(inputDirection => delete tile.inv[inputDirection]);
|
inputDirections.forEach(inputDirection => delete tile.inv[inputDirection]);
|
||||||
tile.timer = LIMITS[tile.type].cooldown;
|
tile.timer = limits.cooldown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue