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