1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-06-13 13:04:03 +00:00

Merge remote-tracking branch 'upstream/master' into logic-pain-fix

This commit is contained in:
dengr1065 2020-08-16 16:59:51 +03:00
commit 55ad849b83
118 changed files with 3084 additions and 1795 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

After

Width:  |  Height:  |  Size: 238 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 574 KiB

After

Width:  |  Height:  |  Size: 567 KiB

View File

@ -258,6 +258,7 @@
<key type="filename">sprites/belt/built/right_8.png</key> <key type="filename">sprites/belt/built/right_8.png</key>
<key type="filename">sprites/belt/built/right_9.png</key> <key type="filename">sprites/belt/built/right_9.png</key>
<key type="filename">sprites/blueprints/constant_signal.png</key> <key type="filename">sprites/blueprints/constant_signal.png</key>
<key type="filename">sprites/blueprints/display.png</key>
<key type="filename">sprites/blueprints/lever.png</key> <key type="filename">sprites/blueprints/lever.png</key>
<key type="filename">sprites/blueprints/logic_gate-not.png</key> <key type="filename">sprites/blueprints/logic_gate-not.png</key>
<key type="filename">sprites/blueprints/logic_gate-or.png</key> <key type="filename">sprites/blueprints/logic_gate-or.png</key>
@ -278,6 +279,7 @@
<key type="filename">sprites/blueprints/underground_belt_exit.png</key> <key type="filename">sprites/blueprints/underground_belt_exit.png</key>
<key type="filename">sprites/blueprints/wire_tunnel.png</key> <key type="filename">sprites/blueprints/wire_tunnel.png</key>
<key type="filename">sprites/buildings/constant_signal.png</key> <key type="filename">sprites/buildings/constant_signal.png</key>
<key type="filename">sprites/buildings/display.png</key>
<key type="filename">sprites/buildings/lever.png</key> <key type="filename">sprites/buildings/lever.png</key>
<key type="filename">sprites/buildings/logic_gate-not.png</key> <key type="filename">sprites/buildings/logic_gate-not.png</key>
<key type="filename">sprites/buildings/logic_gate-or.png</key> <key type="filename">sprites/buildings/logic_gate-or.png</key>
@ -531,6 +533,7 @@
</struct> </struct>
<key type="filename">sprites/wires/boolean_false.png</key> <key type="filename">sprites/wires/boolean_false.png</key>
<key type="filename">sprites/wires/boolean_true.png</key> <key type="filename">sprites/wires/boolean_true.png</key>
<key type="filename">sprites/wires/wires_preview.png</key>
<struct type="IndividualSpriteSettings"> <struct type="IndividualSpriteSettings">
<key>pivotPoint</key> <key>pivotPoint</key>
<point_f>0.5,0.5</point_f> <point_f>0.5,0.5</point_f>
@ -545,6 +548,27 @@
<key>scale9FromFile</key> <key>scale9FromFile</key>
<false/> <false/>
</struct> </struct>
<key type="filename">sprites/wires/display/blue.png</key>
<key type="filename">sprites/wires/display/cyan.png</key>
<key type="filename">sprites/wires/display/green.png</key>
<key type="filename">sprites/wires/display/purple.png</key>
<key type="filename">sprites/wires/display/red.png</key>
<key type="filename">sprites/wires/display/white.png</key>
<key type="filename">sprites/wires/display/yellow.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>11,11,22,22</rect>
<key>scale9Paddings</key>
<rect>11,11,22,22</rect>
<key>scale9FromFile</key>
<false/>
</struct>
</map> </map>
<key>fileList</key> <key>fileList</key>
<array> <array>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -318,9 +318,9 @@ input {
canvas { canvas {
pointer-events: all; pointer-events: all;
image-rendering: pixelated; // image-rendering: pixelated;
// &.smoothed { // &.smoothed {
// } // }1
// &.unsmoothed { // &.unsmoothed {
// } // }
letter-spacing: 0 !important; letter-spacing: 0 !important;

View File

@ -1,5 +1,5 @@
$buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, trash, underground_belt, wire, $buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, trash, underground_belt, wire,
constant_signal, logic_gate, lever, filter, wire_tunnel; constant_signal, logic_gate, lever, filter, wire_tunnel, display;
@each $building in $buildings { @each $building in $buildings {
[data-icon="building_icons/#{$building}.png"] { [data-icon="building_icons/#{$building}.png"] {

View File

@ -13,7 +13,7 @@ import { round1Digit } from "./utils";
const logger = createLogger("buffers"); const logger = createLogger("buffers");
const bufferGcDurationSeconds = 10; const bufferGcDurationSeconds = 5;
export class BufferMaintainer { export class BufferMaintainer {
/** /**
@ -27,6 +27,31 @@ export class BufferMaintainer {
this.iterationIndex = 1; this.iterationIndex = 1;
this.lastIteration = 0; this.lastIteration = 0;
this.root.signals.gameFrameStarted.add(this.update, this);
}
/**
* Returns the buffer stats
*/
getStats() {
let stats = {
rootKeys: 0,
subKeys: 0,
vramBytes: 0,
};
this.cache.forEach((subCache, key) => {
++stats.rootKeys;
subCache.forEach((cacheEntry, subKey) => {
++stats.subKeys;
const canvas = cacheEntry.canvas;
stats.vramBytes += canvas.width * canvas.height * 4;
});
});
return stats;
} }
/** /**

View File

@ -53,6 +53,8 @@ export const globalConfig = {
beltSpeedItemsPerSecond: 2, beltSpeedItemsPerSecond: 2,
minerSpeedItemsPerSecond: 0, // COMPUTED minerSpeedItemsPerSecond: 0, // COMPUTED
defaultItemDiameter: 20,
itemSpacingOnBelts: 0.63, itemSpacingOnBelts: 0.63,
wiresSpeedItemsPerSecond: 6, wiresSpeedItemsPerSecond: 6,

View File

@ -95,5 +95,14 @@ export default {
// Whether to items / s instead of items / m in stats // Whether to items / s instead of items / m in stats
// detailedStatistics: true, // detailedStatistics: true,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Shows detailed information about which atlas is used
// showAtlasInfo: true,
// -----------------------------------------------------------------------------------
// Renders the rotation of all wires
// renderWireRotations: true,
// -----------------------------------------------------------------------------------
// Renders information about wire networks
// renderWireNetworkInfos: true,
// -----------------------------------------------------------------------------------
/* dev:end */ /* dev:end */
}; };

View File

@ -22,9 +22,5 @@ export class DrawParameters {
// FIXME: Not really nice // FIXME: Not really nice
/** @type {GameRoot} */ /** @type {GameRoot} */
this.root = root; this.root = root;
if (G_IS_DEV && globalConfig.debug.testClipping) {
this.visibleRect = this.visibleRect.expandedInAllDirections(-100);
}
} }
} }

View File

@ -3,6 +3,12 @@
* @typedef {import("./draw_parameters").DrawParameters} DrawParameters * @typedef {import("./draw_parameters").DrawParameters} DrawParameters
*/ */
import { globalConfig } from "./config";
import { createLogger } from "./logging";
import { Rectangle } from "./rectangle";
const logger = createLogger("draw_utils");
export function initDrawUtils() { export function initDrawUtils() {
CanvasRenderingContext2D.prototype.beginRoundedRect = function (x, y, w, h, r) { CanvasRenderingContext2D.prototype.beginRoundedRect = function (x, y, w, h, r) {
this.beginPath(); this.beginPath();
@ -52,9 +58,64 @@ export function initDrawUtils() {
* @param {number=} param0.offsetY * @param {number=} param0.offsetY
*/ */
export function drawRotatedSprite({ parameters, sprite, x, y, angle, size, offsetX = 0, offsetY = 0 }) { export function drawRotatedSprite({ parameters, sprite, x, y, angle, size, offsetX = 0, offsetY = 0 }) {
if (angle === 0) {
sprite.drawCachedCentered(parameters, x + offsetX, y + offsetY, size);
return;
}
parameters.context.translate(x, y); parameters.context.translate(x, y);
parameters.context.rotate(angle); parameters.context.rotate(angle);
sprite.drawCachedCentered(parameters, offsetX, offsetY, size, false); sprite.drawCachedCentered(parameters, offsetX, offsetY, size, false);
parameters.context.rotate(-angle); parameters.context.rotate(-angle);
parameters.context.translate(-x, -y); parameters.context.translate(-x, -y);
} }
let warningsShown = 0;
/**
* Draws a sprite with clipping
* @param {object} param0
* @param {DrawParameters} param0.parameters
* @param {HTMLCanvasElement} param0.sprite
* @param {number} param0.x
* @param {number} param0.y
* @param {number} param0.w
* @param {number} param0.h
* @param {number} param0.originalW
* @param {number} param0.originalH
*/
export function drawSpriteClipped({ parameters, sprite, x, y, w, h, originalW, originalH }) {
const rect = new Rectangle(x, y, w, h);
const intersection = rect.getIntersection(parameters.visibleRect);
if (!intersection) {
// Clipped
if (++warningsShown % 200 === 1) {
logger.warn(
"Sprite drawn clipped but it's not on screen - perform culling before (",
warningsShown,
"warnings)"
);
}
if (G_IS_DEV && globalConfig.debug.testClipping) {
parameters.context.fillStyle = "yellow";
parameters.context.fillRect(x, y, w, h);
}
return;
}
parameters.context.drawImage(
sprite,
// src pos and size
((intersection.x - x) / w) * originalW,
((intersection.y - y) / h) * originalH,
(originalW * intersection.w) / w,
(originalH * intersection.h) / h,
// dest pos and size
intersection.x,
intersection.y,
intersection.w,
intersection.h
);
}

View File

@ -377,23 +377,32 @@ export class Rectangle {
} }
/** /**
* Returns a new recangle in tile space which includes all tiles which are visible in this rect * Good for printing stuff
* @param {boolean=} includeHalfTiles
* @returns {Rectangle}
*/ */
toTileCullRectangle(includeHalfTiles = true) { toString() {
let scaled = this.allScaled(1.0 / globalConfig.tileSize); return (
"[x:" +
if (includeHalfTiles) { round2Digits(this.x) +
// Increase rectangle size "| y:" +
scaled = Rectangle.fromTRBL( round2Digits(this.y) +
Math.floor(scaled.y), "| w:" +
Math.ceil(scaled.right()), round2Digits(this.w) +
Math.ceil(scaled.bottom()), "| h:" +
Math.floor(scaled.x) round2Digits(this.h) +
"]"
); );
} }
return scaled; /**
* Returns a new recangle in tile space which includes all tiles which are visible in this rect
* @returns {Rectangle}
*/
toTileCullRectangle() {
return new Rectangle(
Math.floor(this.x / globalConfig.tileSize),
Math.floor(this.y / globalConfig.tileSize),
Math.ceil(this.w / globalConfig.tileSize),
Math.ceil(this.h / globalConfig.tileSize)
);
} }
} }

View File

@ -4,7 +4,7 @@ import { round3Digits } from "./utils";
const floorSpriteCoordinates = false; const floorSpriteCoordinates = false;
export const ORIGINAL_SPRITE_SCALE = "0.5"; export const ORIGINAL_SPRITE_SCALE = "0.75";
export class BaseSprite { export class BaseSprite {
/** /**

View File

@ -0,0 +1,50 @@
import { createLogger } from "./logging";
import { Rectangle } from "./rectangle";
import { globalConfig } from "./config";
const logger = createLogger("stale_areas");
export class StaleAreaDetector {
/**
*
* @param {object} param0
* @param {import("../game/root").GameRoot} param0.root
* @param {string} param0.name The name for reference
* @param {(Rectangle) => void} param0.recomputeMethod Method which recomputes the given area
*/
constructor({ root, name, recomputeMethod }) {
this.root = root;
this.name = name;
this.recomputeMethod = recomputeMethod;
/** @type {Rectangle} */
this.staleArea = null;
}
/**
* Invalidates the given area
* @param {Rectangle} area
*/
invalidate(area) {
// logger.log(this.name, "invalidated", area.toString());
if (this.staleArea) {
this.staleArea = this.staleArea.getUnion(area);
} else {
this.staleArea = area.clone();
}
}
/**
* Updates the stale area
*/
update() {
if (this.staleArea) {
logger.log(this.name, "is recomputing", this.staleArea.toString());
if (G_IS_DEV && globalConfig.debug.renderChanges) {
this.root.hud.parts.changesDebugger.renderChange(this.name, this.staleArea, "#fd145b");
}
this.recomputeMethod(this.staleArea);
this.staleArea = null;
}
}
}

View File

@ -713,3 +713,12 @@ export function rotateDirectionalObject(obj, rotation) {
left: queue[3], left: queue[3],
}; };
} }
/**
* Modulo which works for negative numbers
* @param {number} n
* @param {number} m
*/
export function safeModulo(n, m) {
return ((n % m) + m) % m;
}

View File

@ -1,4 +1,5 @@
import { globalConfig } from "./config"; import { globalConfig } from "./config";
import { safeModulo } from "./utils";
const tileSize = globalConfig.tileSize; const tileSize = globalConfig.tileSize;
const halfTileSize = globalConfig.halfTileSize; const halfTileSize = globalConfig.halfTileSize;
@ -287,6 +288,15 @@ export class Vector {
return dx * dx + dy * dy; return dx * dx + dy * dy;
} }
/**
* Returns x % f, y % f
* @param {number} f
* @returns {Vector} new vector
*/
modScalar(f) {
return new Vector(safeModulo(this.x, f), safeModulo(this.y, f));
}
/** /**
* Computes and returns the center between both points * Computes and returns the center between both points
* @param {Vector} v * @param {Vector} v

View File

@ -1,12 +1,9 @@
import { globalConfig } from "../core/config";
import { DrawParameters } from "../core/draw_parameters"; import { DrawParameters } from "../core/draw_parameters";
import { BasicSerializableObject } from "../savegame/serialization"; import { BasicSerializableObject } from "../savegame/serialization";
/** @enum {string} */ /** @type {ItemType[]} **/
export const enumItemType = { export const itemTypes = ["shape", "color", "boolean"];
shape: "shape",
color: "color",
boolean: "boolean",
};
/** /**
* Class for items on belts etc. Not an entity for performance reasons * Class for items on belts etc. Not an entity for performance reasons
@ -25,10 +22,10 @@ export class BaseItem extends BasicSerializableObject {
return {}; return {};
} }
/** @returns {enumItemType} */ /** @returns {ItemType} **/
getItemType() { getItemType() {
abstract; abstract;
return ""; return "shape";
} }
/** /**
@ -59,9 +56,24 @@ export class BaseItem extends BasicSerializableObject {
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @param {DrawParameters} parameters * @param {DrawParameters} parameters
* @param {number=} size * @param {number=} diameter
*/ */
draw(x, y, parameters, size) {} drawItemCenteredClipped(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
if (parameters.visibleRect.containsCircle(x, y, diameter / 2)) {
this.drawItemCenteredImpl(x, y, parameters, diameter);
}
}
/**
* INTERNAL
* @param {number} x
* @param {number} y
* @param {DrawParameters} parameters
* @param {number=} diameter
*/
drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
abstract;
}
getBackgroundColorAsResource() { getBackgroundColorAsResource() {
abstract; abstract;

View File

@ -8,7 +8,7 @@ import { BasicSerializableObject, types } from "../savegame/serialization";
import { BaseItem } from "./base_item"; import { BaseItem } from "./base_item";
import { Entity } from "./entity"; import { Entity } from "./entity";
import { typeItemSingleton } from "./item_resolver"; import { typeItemSingleton } from "./item_resolver";
import { enumLayer, GameRoot } from "./root"; import { GameRoot } from "./root";
const logger = createLogger("belt_path"); const logger = createLogger("belt_path");
@ -203,7 +203,7 @@ export class BeltPath extends BasicSerializableObject {
const targetEntity = this.root.map.getLayerContentXY( const targetEntity = this.root.map.getLayerContentXY(
ejectSlotTargetWsTile.x, ejectSlotTargetWsTile.x,
ejectSlotTargetWsTile.y, ejectSlotTargetWsTile.y,
enumLayer.regular "regular"
); );
if (targetEntity) { if (targetEntity) {
@ -1194,9 +1194,13 @@ export class BeltPath extends BasicSerializableObject {
const worldPos = staticComp.localTileToWorld(localPos).toWorldSpaceCenterOfTile(); const worldPos = staticComp.localTileToWorld(localPos).toWorldSpaceCenterOfTile();
const distanceAndItem = this.items[currentItemIndex]; const distanceAndItem = this.items[currentItemIndex];
if (parameters.visibleRect.containsCircle(worldPos.x, worldPos.y, 10)) {
distanceAndItem[_item].draw(worldPos.x, worldPos.y, parameters); distanceAndItem[_item].drawItemCenteredClipped(
} worldPos.x,
worldPos.y,
parameters,
globalConfig.defaultItemDiameter
);
// Check for the next item // Check for the next item
currentItemPos += distanceAndItem[_nextDistance]; currentItemPos += distanceAndItem[_nextDistance];

View File

@ -3,7 +3,7 @@ import { Loader } from "../core/loader";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { Vector } from "../core/vector"; import { Vector } from "../core/vector";
import { Entity } from "./entity"; import { Entity } from "./entity";
import { GameRoot, enumLayer } from "./root"; import { GameRoot } from "./root";
import { findNiceIntegerValue } from "../core/utils"; import { findNiceIntegerValue } from "../core/utils";
import { blueprintShape } from "./upgrades"; import { blueprintShape } from "./upgrades";
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
@ -20,11 +20,11 @@ export class Blueprint {
/** /**
* Returns the layer of this blueprint * Returns the layer of this blueprint
* @returns {enumLayer} * @returns {Layer}
*/ */
get layer() { get layer() {
if (this.entities.length === 0) { if (this.entities.length === 0) {
return enumLayer.regular; return "regular";
} }
return this.entities[0].layer; return this.entities[0].layer;
} }
@ -92,7 +92,7 @@ export class Blueprint {
parameters.context.globalAlpha = 1; parameters.context.globalAlpha = 1;
} }
staticComp.drawSpriteOnFullEntityBounds(parameters, staticComp.getBlueprintSprite(), 0, newPos); staticComp.drawSpriteOnBoundsClipped(parameters, staticComp.getBlueprintSprite(), 0, newPos);
} }
parameters.context.globalAlpha = 1; parameters.context.globalAlpha = 1;
} }

View File

@ -89,7 +89,7 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
* @param {Vector} param0.tile * @param {Vector} param0.tile
* @param {number} param0.rotation * @param {number} param0.rotation
* @param {string} param0.variant * @param {string} param0.variant
* @param {string} param0.layer * @param {Layer} param0.layer
* @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array<Entity> }} * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array<Entity> }}
*/ */
computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) { computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) {

View File

@ -2,7 +2,7 @@ import { enumDirection, Vector } from "../../core/vector";
import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
import { Entity } from "../entity"; import { Entity } from "../entity";
import { MetaBuilding } from "../meta_building"; import { MetaBuilding } from "../meta_building";
import { enumLayer, GameRoot } from "../root"; import { GameRoot } from "../root";
import { ConstantSignalComponent } from "../components/constant_signal"; import { ConstantSignalComponent } from "../components/constant_signal";
export class MetaConstantSignalBuilding extends MetaBuilding { export class MetaConstantSignalBuilding extends MetaBuilding {
@ -22,8 +22,9 @@ export class MetaConstantSignalBuilding extends MetaBuilding {
return true; return true;
} }
/** @returns {"wires"} **/
getLayer() { getLayer() {
return enumLayer.wires; return "wires";
} }
getDimensions() { getDimensions() {

View File

@ -8,7 +8,6 @@ import { Entity } from "../entity";
import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
import { GameRoot } from "../root"; import { GameRoot } from "../root";
import { enumHubGoalRewards } from "../tutorial_goals"; import { enumHubGoalRewards } from "../tutorial_goals";
import { enumItemType } from "../base_item";
/** @enum {string} */ /** @enum {string} */
export const enumCutterVariants = { quad: "quad" }; export const enumCutterVariants = { quad: "quad" };
@ -82,7 +81,7 @@ export class MetaCutterBuilding extends MetaBuilding {
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], directions: [enumDirection.bottom],
filter: enumItemType.shape, filter: "shape",
}, },
], ],
}) })

View File

@ -0,0 +1,51 @@
import { enumDirection, Vector } from "../../core/vector";
import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
import { Entity } from "../entity";
import { MetaBuilding } from "../meta_building";
import { GameRoot } from "../root";
import { DisplayComponent } from "../components/display";
export class MetaDisplayBuilding extends MetaBuilding {
constructor() {
super("display");
}
getSilhouetteColor() {
return "#aaaaaa";
}
/**
* @param {GameRoot} root
*/
getIsUnlocked(root) {
// @todo
return true;
}
getDimensions() {
return new Vector(1, 1);
}
getShowWiresLayerPreview() {
return true;
}
/**
* Creates the entity at the given location
* @param {Entity} entity
*/
setupEntityComponents(entity) {
entity.addComponent(
new WiredPinsComponent({
slots: [
{
pos: new Vector(0, 0),
direction: enumDirection.bottom,
type: enumPinSlotType.logicalAcceptor,
},
],
})
);
entity.addComponent(new DisplayComponent());
}
}

View File

@ -29,6 +29,10 @@ export class MetaFilterBuilding extends MetaBuilding {
return new Vector(2, 1); return new Vector(2, 1);
} }
getShowWiresLayerPreview() {
return true;
}
/** /**
* Creates the entity at the given location * Creates the entity at the given location
* @param {Entity} entity * @param {Entity} entity

View File

@ -1,5 +1,4 @@
import { enumDirection, Vector } from "../../core/vector"; import { enumDirection, Vector } from "../../core/vector";
import { enumItemType } from "../base_item";
import { HubComponent } from "../components/hub"; import { HubComponent } from "../components/hub";
import { ItemAcceptorComponent } from "../components/item_acceptor"; import { ItemAcceptorComponent } from "../components/item_acceptor";
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
@ -68,72 +67,72 @@ export class MetaHubBuilding extends MetaBuilding {
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.top, enumDirection.left], directions: [enumDirection.top, enumDirection.left],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(1, 0), pos: new Vector(1, 0),
directions: [enumDirection.top], directions: [enumDirection.top],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(2, 0), pos: new Vector(2, 0),
directions: [enumDirection.top], directions: [enumDirection.top],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(3, 0), pos: new Vector(3, 0),
directions: [enumDirection.top, enumDirection.right], directions: [enumDirection.top, enumDirection.right],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(0, 3), pos: new Vector(0, 3),
directions: [enumDirection.bottom, enumDirection.left], directions: [enumDirection.bottom, enumDirection.left],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(1, 3), pos: new Vector(1, 3),
directions: [enumDirection.bottom], directions: [enumDirection.bottom],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(2, 3), pos: new Vector(2, 3),
directions: [enumDirection.bottom], directions: [enumDirection.bottom],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(3, 3), pos: new Vector(3, 3),
directions: [enumDirection.bottom, enumDirection.right], directions: [enumDirection.bottom, enumDirection.right],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(0, 1), pos: new Vector(0, 1),
directions: [enumDirection.left], directions: [enumDirection.left],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(0, 2), pos: new Vector(0, 2),
directions: [enumDirection.left], directions: [enumDirection.left],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(0, 3), pos: new Vector(0, 3),
directions: [enumDirection.left], directions: [enumDirection.left],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(3, 1), pos: new Vector(3, 1),
directions: [enumDirection.right], directions: [enumDirection.right],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(3, 2), pos: new Vector(3, 2),
directions: [enumDirection.right], directions: [enumDirection.right],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(3, 3), pos: new Vector(3, 3),
directions: [enumDirection.right], directions: [enumDirection.right],
filter: enumItemType.shape, filter: "shape",
}, },
], ],
}) })

View File

@ -35,6 +35,10 @@ export class MetaLeverBuilding extends MetaBuilding {
return null; return null;
} }
getShowWiresLayerPreview() {
return true;
}
/** /**
* Creates the entity at the given location * Creates the entity at the given location
* @param {Entity} entity * @param {Entity} entity

View File

@ -2,7 +2,7 @@ import { enumDirection, Vector } from "../../core/vector";
import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
import { Entity } from "../entity"; import { Entity } from "../entity";
import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
import { enumLayer, GameRoot } from "../root"; import { GameRoot } from "../root";
import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate"; import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate";
/** @enum {string} */ /** @enum {string} */
@ -39,8 +39,9 @@ export class MetaLogicGateBuilding extends MetaBuilding {
return true; return true;
} }
/** @returns {"wires"} **/
getLayer() { getLayer() {
return enumLayer.wires; return "wires";
} }
getDimensions() { getDimensions() {

View File

@ -1,7 +1,6 @@
import { formatItemsPerSecond } from "../../core/utils"; import { formatItemsPerSecond } from "../../core/utils";
import { enumDirection, Vector } from "../../core/vector"; import { enumDirection, Vector } from "../../core/vector";
import { T } from "../../translations"; import { T } from "../../translations";
import { enumItemType } from "../base_item";
import { ItemAcceptorComponent } from "../components/item_acceptor"; import { ItemAcceptorComponent } from "../components/item_acceptor";
import { ItemEjectorComponent } from "../components/item_ejector"; import { ItemEjectorComponent } from "../components/item_ejector";
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
@ -63,12 +62,12 @@ export class MetaMixerBuilding extends MetaBuilding {
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], directions: [enumDirection.bottom],
filter: enumItemType.color, filter: "color",
}, },
{ {
pos: new Vector(1, 0), pos: new Vector(1, 0),
directions: [enumDirection.bottom], directions: [enumDirection.bottom],
filter: enumItemType.color, filter: "color",
}, },
], ],
}) })

View File

@ -8,7 +8,6 @@ import { Entity } from "../entity";
import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
import { GameRoot } from "../root"; import { GameRoot } from "../root";
import { enumHubGoalRewards } from "../tutorial_goals"; import { enumHubGoalRewards } from "../tutorial_goals";
import { enumItemType } from "../base_item";
import { WiredPinsComponent, enumPinSlotType } from "../components/wired_pins"; import { WiredPinsComponent, enumPinSlotType } from "../components/wired_pins";
import { ProcessingRequirementComponent } from "../components/processing_requirement"; import { ProcessingRequirementComponent } from "../components/processing_requirement";
@ -100,12 +99,12 @@ export class MetaPainterBuilding extends MetaBuilding {
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.left], directions: [enumDirection.left],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(1, 0), pos: new Vector(1, 0),
directions: [enumDirection.top], directions: [enumDirection.top],
filter: enumItemType.color, filter: "color",
}, },
], ],
}) })
@ -133,14 +132,14 @@ export class MetaPainterBuilding extends MetaBuilding {
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.left], directions: [enumDirection.left],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(1, 0), pos: new Vector(1, 0),
directions: [ directions: [
variant === defaultBuildingVariant ? enumDirection.top : enumDirection.bottom, variant === defaultBuildingVariant ? enumDirection.top : enumDirection.bottom,
], ],
filter: enumItemType.color, filter: "color",
}, },
]); ]);
@ -163,17 +162,17 @@ export class MetaPainterBuilding extends MetaBuilding {
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.left], directions: [enumDirection.left],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(0, 1), pos: new Vector(0, 1),
directions: [enumDirection.left], directions: [enumDirection.left],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(1, 0), pos: new Vector(1, 0),
directions: [enumDirection.top], directions: [enumDirection.top],
filter: enumItemType.color, filter: "color",
}, },
]); ]);
@ -223,27 +222,27 @@ export class MetaPainterBuilding extends MetaBuilding {
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.left], directions: [enumDirection.left],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], directions: [enumDirection.bottom],
filter: enumItemType.color, filter: "color",
}, },
{ {
pos: new Vector(1, 0), pos: new Vector(1, 0),
directions: [enumDirection.bottom], directions: [enumDirection.bottom],
filter: enumItemType.color, filter: "color",
}, },
{ {
pos: new Vector(2, 0), pos: new Vector(2, 0),
directions: [enumDirection.bottom], directions: [enumDirection.bottom],
filter: enumItemType.color, filter: "color",
}, },
{ {
pos: new Vector(3, 0), pos: new Vector(3, 0),
directions: [enumDirection.bottom], directions: [enumDirection.bottom],
filter: enumItemType.color, filter: "color",
}, },
]); ]);

View File

@ -8,7 +8,6 @@ import { Entity } from "../entity";
import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
import { GameRoot } from "../root"; import { GameRoot } from "../root";
import { enumHubGoalRewards } from "../tutorial_goals"; import { enumHubGoalRewards } from "../tutorial_goals";
import { enumItemType } from "../base_item";
/** @enum {string} */ /** @enum {string} */
export const enumRotaterVariants = { ccw: "ccw", fl: "fl" }; export const enumRotaterVariants = { ccw: "ccw", fl: "fl" };
@ -89,7 +88,7 @@ export class MetaRotaterBuilding extends MetaBuilding {
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], directions: [enumDirection.bottom],
filter: enumItemType.shape, filter: "shape",
}, },
], ],
}) })

View File

@ -8,7 +8,6 @@ import { Entity } from "../entity";
import { MetaBuilding } from "../meta_building"; import { MetaBuilding } from "../meta_building";
import { GameRoot } from "../root"; import { GameRoot } from "../root";
import { enumHubGoalRewards } from "../tutorial_goals"; import { enumHubGoalRewards } from "../tutorial_goals";
import { enumItemType } from "../base_item";
export class MetaStackerBuilding extends MetaBuilding { export class MetaStackerBuilding extends MetaBuilding {
constructor() { constructor() {
@ -63,12 +62,12 @@ export class MetaStackerBuilding extends MetaBuilding {
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], directions: [enumDirection.bottom],
filter: enumItemType.shape, filter: "shape",
}, },
{ {
pos: new Vector(1, 0), pos: new Vector(1, 0),
directions: [enumDirection.bottom], directions: [enumDirection.bottom],
filter: enumItemType.shape, filter: "shape",
}, },
], ],
}) })

View File

@ -5,7 +5,7 @@ import { ItemEjectorComponent } from "../components/item_ejector";
import { enumUndergroundBeltMode, UndergroundBeltComponent } from "../components/underground_belt"; import { enumUndergroundBeltMode, UndergroundBeltComponent } from "../components/underground_belt";
import { Entity } from "../entity"; import { Entity } from "../entity";
import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
import { GameRoot, enumLayer } from "../root"; import { GameRoot } from "../root";
import { globalConfig } from "../../core/config"; import { globalConfig } from "../../core/config";
import { enumHubGoalRewards } from "../tutorial_goals"; import { enumHubGoalRewards } from "../tutorial_goals";
import { formatItemsPerSecond, generateMatrixRotations } from "../../core/utils"; import { formatItemsPerSecond, generateMatrixRotations } from "../../core/utils";
@ -171,7 +171,7 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding {
* @param {Vector} param0.tile * @param {Vector} param0.tile
* @param {number} param0.rotation * @param {number} param0.rotation
* @param {string} param0.variant * @param {string} param0.variant
* @param {string} param0.layer * @param {Layer} param0.layer
* @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array<Entity> }} * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array<Entity> }}
*/ */
computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) { computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) {
@ -190,7 +190,7 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding {
tile = tile.addScalars(searchVector.x, searchVector.y); tile = tile.addScalars(searchVector.x, searchVector.y);
/* WIRES: FIXME */ /* WIRES: FIXME */
const contents = root.map.getTileContent(tile, enumLayer.regular); const contents = root.map.getTileContent(tile, "regular");
if (contents) { if (contents) {
const undergroundComp = contents.components.UndergroundBelt; const undergroundComp = contents.components.UndergroundBelt;
if (undergroundComp && undergroundComp.tier === tier) { if (undergroundComp && undergroundComp.tier === tier) {

View File

@ -5,7 +5,7 @@ import { SOUNDS } from "../../platform/sound";
import { enumWireType, WireComponent } from "../components/wire"; import { enumWireType, WireComponent } from "../components/wire";
import { Entity } from "../entity"; import { Entity } from "../entity";
import { MetaBuilding } from "../meta_building"; import { MetaBuilding } from "../meta_building";
import { enumLayer, GameRoot } from "../root"; import { GameRoot } from "../root";
export const arrayWireRotationVariantToType = [ export const arrayWireRotationVariantToType = [
enumWireType.regular, enumWireType.regular,
@ -50,8 +50,9 @@ export class MetaWireBuilding extends MetaBuilding {
return true; return true;
} }
/** @returns {"wires"} **/
getLayer() { getLayer() {
return enumLayer.wires; return "wires";
} }
getSprite() { getSprite() {

View File

@ -1,8 +1,19 @@
import { Vector } from "../../core/vector"; import { Vector } from "../../core/vector";
import { Entity } from "../entity"; import { Entity } from "../entity";
import { MetaBuilding } from "../meta_building"; import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
import { GameRoot, enumLayer } from "../root"; import { GameRoot } from "../root";
import { WireTunnelComponent } from "../components/wire_tunnel"; import { WireTunnelComponent } from "../components/wire_tunnel";
import { generateMatrixRotations } from "../../core/utils";
/** @enum {string} */
export const enumWireTunnelVariants = {
coating: "coating",
};
const wireTunnelOverlayMatrices = {
[defaultBuildingVariant]: generateMatrixRotations([0, 1, 0, 1, 1, 1, 0, 1, 0]),
[enumWireTunnelVariants.coating]: generateMatrixRotations([0, 1, 0, 0, 1, 0, 0, 1, 0]),
};
export class MetaWireTunnelBuilding extends MetaBuilding { export class MetaWireTunnelBuilding extends MetaBuilding {
constructor() { constructor() {
@ -10,7 +21,7 @@ export class MetaWireTunnelBuilding extends MetaBuilding {
} }
getSilhouetteColor() { getSilhouetteColor() {
return "#25fff2"; return "#777a86";
} }
/** /**
@ -21,16 +32,40 @@ export class MetaWireTunnelBuilding extends MetaBuilding {
return true; return true;
} }
getIsRotateable() { /**
return false; *
* @param {number} rotation
* @param {number} rotationVariant
* @param {string} variant
* @param {Entity} entity
*/
getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) {
return wireTunnelOverlayMatrices[variant][rotation];
}
getIsRotateable(variant) {
return variant !== defaultBuildingVariant;
} }
getDimensions() { getDimensions() {
return new Vector(1, 1); return new Vector(1, 1);
} }
getAvailableVariants() {
return [defaultBuildingVariant, enumWireTunnelVariants.coating];
}
/** @returns {"wires"} **/
getLayer() { getLayer() {
return enumLayer.wires; return "wires";
}
getRotateAutomaticallyWhilePlacing() {
return true;
}
getStayInPlacementMode() {
return true;
} }
/** /**
@ -38,6 +73,15 @@ export class MetaWireTunnelBuilding extends MetaBuilding {
* @param {Entity} entity * @param {Entity} entity
*/ */
setupEntityComponents(entity) { setupEntityComponents(entity) {
entity.addComponent(new WireTunnelComponent()); entity.addComponent(new WireTunnelComponent({}));
}
/**
* @param {Entity} entity
* @param {number} rotationVariant
* @param {string} variant
*/
updateVariants(entity, rotationVariant, variant) {
entity.components.WireTunnel.multipleDirections = variant === defaultBuildingVariant;
} }
} }

View File

@ -15,7 +15,11 @@ import { ConstantSignalComponent } from "./components/constant_signal";
import { LogicGateComponent } from "./components/logic_gate"; import { LogicGateComponent } from "./components/logic_gate";
import { LeverComponent } from "./components/lever"; import { LeverComponent } from "./components/lever";
import { WireTunnelComponent } from "./components/wire_tunnel"; import { WireTunnelComponent } from "./components/wire_tunnel";
<<<<<<< HEAD
import { ProcessingRequirementComponent } from "./components/processing_requirement"; import { ProcessingRequirementComponent } from "./components/processing_requirement";
=======
import { DisplayComponent } from "./components/display";
>>>>>>> upstream/master
export function initComponentRegistry() { export function initComponentRegistry() {
gComponentRegistry.register(StaticMapEntityComponent); gComponentRegistry.register(StaticMapEntityComponent);
@ -35,6 +39,7 @@ export function initComponentRegistry() {
gComponentRegistry.register(LeverComponent); gComponentRegistry.register(LeverComponent);
gComponentRegistry.register(WireTunnelComponent); gComponentRegistry.register(WireTunnelComponent);
gComponentRegistry.register(ProcessingRequirementComponent); gComponentRegistry.register(ProcessingRequirementComponent);
gComponentRegistry.register(DisplayComponent);
// IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS // IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS

View File

@ -0,0 +1,11 @@
import { Component } from "../component";
export class DisplayComponent extends Component {
static getId() {
return "Display";
}
duplicateWithoutContents() {
return new DisplayComponent();
}
}

View File

@ -1,12 +1,12 @@
import { enumDirection, enumInvertedDirections, Vector } from "../../core/vector"; import { enumDirection, enumInvertedDirections, Vector } from "../../core/vector";
import { types } from "../../savegame/serialization"; import { types } from "../../savegame/serialization";
import { BaseItem, enumItemType } from "../base_item"; import { BaseItem } from "../base_item";
import { Component } from "../component"; import { Component } from "../component";
/** @typedef {{ /** @typedef {{
* pos: Vector, * pos: Vector,
* directions: enumDirection[], * directions: enumDirection[],
* filter?: enumItemType * filter?: ItemType
* }} ItemAcceptorSlot */ * }} ItemAcceptorSlot */
/** /**
@ -20,7 +20,7 @@ import { Component } from "../component";
/** @typedef {{ /** @typedef {{
* pos: Vector, * pos: Vector,
* directions: enumDirection[], * directions: enumDirection[],
* filter?: enumItemType * filter?: ItemType
* }} ItemAcceptorSlotConfig */ * }} ItemAcceptorSlotConfig */
export class ItemAcceptorComponent extends Component { export class ItemAcceptorComponent extends Component {
@ -74,7 +74,7 @@ export class ItemAcceptorComponent extends Component {
pos: slot.pos, pos: slot.pos,
directions: slot.directions, directions: slot.directions,
// Which type of item to accept (shape | color | all) @see enumItemType // Which type of item to accept (shape | color | all) @see ItemType
filter: slot.filter, filter: slot.filter,
}); });
} }

View File

@ -162,8 +162,9 @@ export class StaticMapEntityComponent extends Component {
* @returns {Vector} * @returns {Vector}
*/ */
localTileToWorld(localTile) { localTileToWorld(localTile) {
const result = this.applyRotationToVector(localTile); const result = localTile.rotateFastMultipleOf90(this.rotation);
result.addInplace(this.origin); result.x += this.origin.x;
result.y += this.origin.y;
return result; return result;
} }
@ -235,7 +236,7 @@ export class StaticMapEntityComponent extends Component {
* @param {number=} extrudePixels How many pixels to extrude the sprite * @param {number=} extrudePixels How many pixels to extrude the sprite
* @param {Vector=} overridePosition Whether to drwa the entity at a different location * @param {Vector=} overridePosition Whether to drwa the entity at a different location
*/ */
drawSpriteOnFullEntityBounds(parameters, sprite, extrudePixels = 0, overridePosition = null) { drawSpriteOnBoundsClipped(parameters, sprite, extrudePixels = 0, overridePosition = null) {
if (!this.shouldBeDrawn(parameters) && !overridePosition) { if (!this.shouldBeDrawn(parameters) && !overridePosition) {
return; return;
} }
@ -255,8 +256,7 @@ export class StaticMapEntityComponent extends Component {
worldX - extrudePixels * size.x, worldX - extrudePixels * size.x,
worldY - extrudePixels * size.y, worldY - extrudePixels * size.y,
globalConfig.tileSize * size.x + 2 * extrudePixels * size.x, globalConfig.tileSize * size.x + 2 * extrudePixels * size.x,
globalConfig.tileSize * size.y + 2 * extrudePixels * size.y, globalConfig.tileSize * size.y + 2 * extrudePixels * size.y
false
); );
} else { } else {
const rotationCenterX = worldX + globalConfig.halfTileSize; const rotationCenterX = worldX + globalConfig.halfTileSize;
@ -264,16 +264,14 @@ export class StaticMapEntityComponent extends Component {
parameters.context.translate(rotationCenterX, rotationCenterY); parameters.context.translate(rotationCenterX, rotationCenterY);
parameters.context.rotate(Math.radians(this.rotation)); parameters.context.rotate(Math.radians(this.rotation));
sprite.drawCached( sprite.drawCached(
parameters, parameters,
-globalConfig.halfTileSize - extrudePixels * size.x, -globalConfig.halfTileSize - extrudePixels * size.x,
-globalConfig.halfTileSize - extrudePixels * size.y, -globalConfig.halfTileSize - extrudePixels * size.y,
globalConfig.tileSize * size.x + 2 * extrudePixels * size.x, globalConfig.tileSize * size.x + 2 * extrudePixels * size.x,
globalConfig.tileSize * size.y + 2 * extrudePixels * size.y, globalConfig.tileSize * size.y + 2 * extrudePixels * size.y,
false false // no clipping possible here
); );
parameters.context.rotate(-Math.radians(this.rotation)); parameters.context.rotate(-Math.radians(this.rotation));
parameters.context.translate(-rotationCenterX, -rotationCenterY); parameters.context.translate(-rotationCenterX, -rotationCenterY);
} }

View File

@ -1,5 +1,5 @@
import { types } from "../../savegame/serialization"; import { types } from "../../savegame/serialization";
import { BaseItem, enumItemType } from "../base_item"; import { BaseItem } from "../base_item";
import { Component } from "../component"; import { Component } from "../component";
import { typeItemSingleton } from "../item_resolver"; import { typeItemSingleton } from "../item_resolver";
import { ColorItem } from "../items/color_item"; import { ColorItem } from "../items/color_item";
@ -65,11 +65,11 @@ export class StorageComponent extends Component {
return false; return false;
} }
if (itemType === enumItemType.color) { if (itemType === "color") {
return /** @type {ColorItem} */ (this.storedItem).color === /** @type {ColorItem} */ (item).color; return /** @type {ColorItem} */ (this.storedItem).color === /** @type {ColorItem} */ (item).color;
} }
if (itemType === enumItemType.shape) { if (itemType === "shape") {
return ( return (
/** @type {ShapeItem} */ (this.storedItem).definition.getHash() === /** @type {ShapeItem} */ (this.storedItem).definition.getHash() ===
/** @type {ShapeItem} */ (item).definition.getHash() /** @type {ShapeItem} */ (item).definition.getHash()

View File

@ -6,6 +6,21 @@ export class WireTunnelComponent extends Component {
} }
duplicateWithoutContents() { duplicateWithoutContents() {
return new WireTunnelComponent(); return new WireTunnelComponent({ multipleDirections: this.multipleDirections });
}
/**
* @param {object} param0
* @param {boolean=} param0.multipleDirections
*/
constructor({ multipleDirections = true }) {
super();
this.multipleDirections = multipleDirections;
/**
* Linked network, only if its not multiple directions
* @type {Array<import("../systems/wire").WireNetwork>}
*/
this.linkedNetworks = [];
} }
} }

View File

@ -1,6 +1,8 @@
import { enumDirection, Vector } from "../../core/vector"; import { enumDirection, Vector } from "../../core/vector";
import { BaseItem } from "../base_item"; import { BaseItem } from "../base_item";
import { Component } from "../component"; import { Component } from "../component";
import { types } from "../../savegame/serialization";
import { typeItemSingleton } from "../item_resolver";
/** @enum {string} */ /** @enum {string} */
export const enumPinSlotType = { export const enumPinSlotType = {
@ -27,6 +29,16 @@ export class WiredPinsComponent extends Component {
return "WiredPins"; return "WiredPins";
} }
static getSchema() {
return {
slots: types.array(
types.structured({
value: types.nullable(typeItemSingleton),
})
),
};
}
/** /**
* *
* @param {object} param0 * @param {object} param0

View File

@ -9,7 +9,7 @@ import { DrawParameters } from "../core/draw_parameters";
import { gMetaBuildingRegistry } from "../core/global_registries"; import { gMetaBuildingRegistry } from "../core/global_registries";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { Rectangle } from "../core/rectangle"; import { Rectangle } from "../core/rectangle";
import { randomInt, round2Digits } from "../core/utils"; import { randomInt, round2Digits, round3Digits } from "../core/utils";
import { Vector } from "../core/vector"; import { Vector } from "../core/vector";
import { Savegame } from "../savegame/savegame"; import { Savegame } from "../savegame/savegame";
import { SavegameSerializer } from "../savegame/savegame_serializer"; import { SavegameSerializer } from "../savegame/savegame_serializer";
@ -26,7 +26,7 @@ import { GameLogic } from "./logic";
import { MapView } from "./map_view"; import { MapView } from "./map_view";
import { defaultBuildingVariant } from "./meta_building"; import { defaultBuildingVariant } from "./meta_building";
import { ProductionAnalytics } from "./production_analytics"; import { ProductionAnalytics } from "./production_analytics";
import { enumLayer, GameRoot } from "./root"; import { GameRoot } from "./root";
import { ShapeDefinitionManager } from "./shape_definition_manager"; import { ShapeDefinitionManager } from "./shape_definition_manager";
import { SoundProxy } from "./sound_proxy"; import { SoundProxy } from "./sound_proxy";
import { GameTime } from "./time/game_time"; import { GameTime } from "./time/game_time";
@ -329,8 +329,7 @@ export class GameCore {
return; return;
} }
// Update buffers as the very first this.root.signals.gameFrameStarted.dispatch();
root.buffers.update();
root.queue.requireRedraw = false; root.queue.requireRedraw = false;
@ -369,6 +368,13 @@ export class GameCore {
} }
// Transform to world space // Transform to world space
if (G_IS_DEV && globalConfig.debug.testClipping) {
params.visibleRect = params.visibleRect.expandedInAllDirections(
-200 / this.root.camera.zoomLevel
);
}
root.camera.transform(context); root.camera.transform(context);
assert(context.globalAlpha === 1.0, "Global alpha not 1 on frame start"); assert(context.globalAlpha === 1.0, "Global alpha not 1 on frame start");
@ -383,36 +389,24 @@ export class GameCore {
// Map overview // Map overview
root.map.drawOverlay(params); root.map.drawOverlay(params);
} else { } else {
// Background (grid, resources, etc)
root.map.drawBackground(params); root.map.drawBackground(params);
// Underlays for splitters / balancers
systems.beltUnderlays.drawUnderlays(params);
// Belt items // Belt items
systems.belt.drawBeltItems(params); systems.belt.drawBeltItems(params);
// Items being ejected / accepted currently (animations) // Miner & Static map entities etc.
systems.itemEjector.draw(params);
systems.itemAcceptor.draw(params);
// Miner & Static map entities
root.map.drawForeground(params); root.map.drawForeground(params);
// HUB Overlay // HUB Overlay
systems.hub.draw(params); systems.hub.draw(params);
// Storage items
systems.storage.draw(params);
// Green wires overlay // Green wires overlay
root.hud.parts.wiresOverlay.draw(params); root.hud.parts.wiresOverlay.draw(params);
if (this.root.currentLayer === enumLayer.wires) { if (this.root.currentLayer === "wires") {
// Static map entities // Static map entities
root.map.drawWiresForegroundLayer(params); root.map.drawWiresForegroundLayer(params);
// pins
systems.wiredPins.draw(params);
} }
} }
@ -439,6 +433,9 @@ export class GameCore {
params.zoomLevel = 1; params.zoomLevel = 1;
params.desiredAtlasScale = ORIGINAL_SPRITE_SCALE; params.desiredAtlasScale = ORIGINAL_SPRITE_SCALE;
params.visibleRect = new Rectangle(0, 0, this.root.gameWidth, this.root.gameHeight); params.visibleRect = new Rectangle(0, 0, this.root.gameWidth, this.root.gameHeight);
if (G_IS_DEV && globalConfig.debug.testClipping) {
params.visibleRect = params.visibleRect.expandedInAllDirections(-200);
}
// Draw overlays, those are screen space // Draw overlays, those are screen space
root.hud.drawOverlays(params); root.hud.drawOverlays(params);
@ -457,7 +454,7 @@ export class GameCore {
if (G_IS_DEV && globalConfig.debug.showAtlasInfo) { if (G_IS_DEV && globalConfig.debug.showAtlasInfo) {
context.font = "13px GameFont"; context.font = "13px GameFont";
context.fillStyle = "yellow"; context.fillStyle = "blue";
context.fillText( context.fillText(
"Atlas: " + "Atlas: " +
desiredAtlasScale + desiredAtlasScale +
@ -465,9 +462,31 @@ export class GameCore {
round2Digits(zoomLevel) + round2Digits(zoomLevel) +
" / Effective Zoom: " + " / Effective Zoom: " +
round2Digits(effectiveZoomLevel), round2Digits(effectiveZoomLevel),
200, 20,
20 600
);
const stats = this.root.buffers.getStats();
context.fillText(
"Buffers: " +
stats.rootKeys +
" root keys, " +
stats.subKeys +
" sub keys / buffers / VRAM: " +
round2Digits(stats.vramBytes / (1024 * 1024)) +
" MB",
20,
620
); );
} }
if (G_IS_DEV && globalConfig.debug.testClipping) {
context.strokeStyle = "red";
context.lineWidth = 1;
context.beginPath();
context.rect(200, 200, this.root.gameWidth - 400, this.root.gameHeight - 400);
context.stroke();
}
} }
} }

View File

@ -3,7 +3,7 @@ import { DrawParameters } from "../core/draw_parameters";
import { Component } from "./component"; import { Component } from "./component";
/* typehints:end */ /* typehints:end */
import { GameRoot, enumLayer } from "./root"; import { GameRoot } from "./root";
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { enumDirectionToVector, enumDirectionToAngle } from "../core/vector"; import { enumDirectionToVector, enumDirectionToAngle } from "../core/vector";
import { BasicSerializableObject, types } from "../savegame/serialization"; import { BasicSerializableObject, types } from "../savegame/serialization";
@ -36,8 +36,9 @@ export class Entity extends BasicSerializableObject {
/** /**
* On which layer this entity is * On which layer this entity is
* @type {Layer}
*/ */
this.layer = enumLayer.regular; this.layer = "regular";
/** /**
* Internal entity unique id, set by the @see EntityManager * Internal entity unique id, set by the @see EntityManager

View File

@ -16,6 +16,7 @@ import { LogicGateComponent } from "./components/logic_gate";
import { LeverComponent } from "./components/lever"; import { LeverComponent } from "./components/lever";
import { WireTunnelComponent } from "./components/wire_tunnel"; import { WireTunnelComponent } from "./components/wire_tunnel";
import { ProcessingRequirementComponent } from "./components/processing_requirement"; import { ProcessingRequirementComponent } from "./components/processing_requirement";
import { DisplayComponent } from "./components/display";
/* typehints:end */ /* typehints:end */
/** /**
@ -77,6 +78,9 @@ export class EntityComponentStorage {
/** @type {ProcessingRequirementComponent} */ /** @type {ProcessingRequirementComponent} */
this.ProcessingRequirement; this.ProcessingRequirement;
/** @type {DisplayComponent} */
this.Display;
/* typehints:end */ /* typehints:end */
} }
} }

View File

@ -19,6 +19,7 @@ import { WireSystem } from "./systems/wire";
import { ConstantSignalSystem } from "./systems/constant_signal"; import { ConstantSignalSystem } from "./systems/constant_signal";
import { LogicGateSystem } from "./systems/logic_gate"; import { LogicGateSystem } from "./systems/logic_gate";
import { LeverSystem } from "./systems/lever"; import { LeverSystem } from "./systems/lever";
import { DisplaySystem } from "./systems/display";
const logger = createLogger("game_system_manager"); const logger = createLogger("game_system_manager");
@ -80,6 +81,9 @@ export class GameSystemManager {
/** @type {LeverSystem} */ /** @type {LeverSystem} */
lever: null, lever: null,
/** @type {DisplaySystem} */
display: null,
/* typehints:end */ /* typehints:end */
}; };
this.systemUpdateOrder = []; this.systemUpdateOrder = [];
@ -137,6 +141,8 @@ export class GameSystemManager {
// Wires must be after all gate, signal etc logic! // Wires must be after all gate, signal etc logic!
add("wire", WireSystem); add("wire", WireSystem);
add("display", DisplaySystem);
logger.log("📦 There are", this.systemUpdateOrder.length, "game systems"); logger.log("📦 There are", this.systemUpdateOrder.length, "game systems");
} }

View File

@ -6,8 +6,7 @@ import { Entity } from "./entity";
import { GameRoot } from "./root"; import { GameRoot } from "./root";
import { GameSystem } from "./game_system"; import { GameSystem } from "./game_system";
import { arrayDelete, arrayDeleteValue } from "../core/utils"; import { arrayDelete, arrayDeleteValue } from "../core/utils";
import { DrawParameters } from "../core/draw_parameters";
import { globalConfig } from "../core/config";
export class GameSystemWithFilter extends GameSystem { export class GameSystemWithFilter extends GameSystem {
/** /**
* Constructs a new game system with the given component filter. It will process * Constructs a new game system with the given component filter. It will process
@ -35,80 +34,6 @@ export class GameSystemWithFilter extends GameSystem {
this.root.signals.bulkOperationFinished.add(this.refreshCaches, this); this.root.signals.bulkOperationFinished.add(this.refreshCaches, this);
} }
/**
* Calls a function for each matching entity on the screen, useful for drawing them
* @param {DrawParameters} parameters
* @param {function} callback
*/
forEachMatchingEntityOnScreen(parameters, callback) {
const cullRange = parameters.visibleRect.toTileCullRectangle();
if (this.allEntities.length < 100) {
// So, its much quicker to simply perform per-entity checking
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
if (cullRange.containsRect(entity.components.StaticMapEntity.getTileSpaceBounds())) {
callback(parameters, entity);
}
}
return;
}
const top = cullRange.top();
const right = cullRange.right();
const bottom = cullRange.bottom();
const left = cullRange.left();
const border = 1;
const minY = top - border;
const maxY = bottom + border;
const minX = left - border;
const maxX = right + border - 1;
const map = this.root.map;
let seenUids = new Set();
const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize);
const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize);
const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize);
const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize);
const requiredComponents = this.requiredComponentIds;
// Render y from top down for proper blending
for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) {
for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) {
const chunk = map.getChunk(chunkX, chunkY, false);
if (!chunk) {
continue;
}
// BIG TODO: CULLING ON AN ENTITY BASIS
const entities = chunk.containedEntities;
entityLoop: for (let i = 0; i < entities.length; ++i) {
const entity = entities[i];
// Avoid drawing twice
if (seenUids.has(entity.uid)) {
continue;
}
seenUids.add(entity.uid);
for (let i = 0; i < requiredComponents.length; ++i) {
if (!entity.components[requiredComponents[i]]) {
continue entityLoop;
}
}
callback(parameters, entity);
}
}
}
}
/** /**
* @param {Entity} entity * @param {Entity} entity
*/ */

View File

@ -3,7 +3,7 @@ import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/ut
import { BasicSerializableObject, types } from "../savegame/serialization"; import { BasicSerializableObject, types } from "../savegame/serialization";
import { enumColors } from "./colors"; import { enumColors } from "./colors";
import { enumItemProcessorTypes } from "./components/item_processor"; import { enumItemProcessorTypes } from "./components/item_processor";
import { GameRoot, enumLayer } from "./root"; import { GameRoot } from "./root";
import { enumSubShape, ShapeDefinition } from "./shape_definition"; import { enumSubShape, ShapeDefinition } from "./shape_definition";
import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals"; import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals";
import { UPGRADES } from "./upgrades"; import { UPGRADES } from "./upgrades";

View File

@ -42,6 +42,7 @@ import { HUDSandboxController } from "./parts/sandbox_controller";
import { HUDWiresToolbar } from "./parts/wires_toolbar"; import { HUDWiresToolbar } from "./parts/wires_toolbar";
import { HUDWireInfo } from "./parts/wire_info"; import { HUDWireInfo } from "./parts/wire_info";
import { HUDLeverToggle } from "./parts/lever_toggle"; import { HUDLeverToggle } from "./parts/lever_toggle";
import { HUDLayerPreview } from "./parts/layer_preview";
export class GameHUD { export class GameHUD {
/** /**
@ -80,6 +81,7 @@ export class GameHUD {
shapeViewer: new HUDShapeViewer(this.root), shapeViewer: new HUDShapeViewer(this.root),
wiresOverlay: new HUDWiresOverlay(this.root), wiresOverlay: new HUDWiresOverlay(this.root),
layerPreview: new HUDLayerPreview(this.root),
// Typing hints // Typing hints
/* typehints:start */ /* typehints:start */

View File

@ -11,7 +11,6 @@ import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach"; import { DynamicDomAttach } from "../dynamic_dom_attach";
import { Blueprint } from "../../blueprint"; import { Blueprint } from "../../blueprint";
import { SOUNDS } from "../../../platform/sound"; import { SOUNDS } from "../../../platform/sound";
import { enumLayer } from "../../root";
export class HUDBlueprintPlacer extends BaseHUDPart { export class HUDBlueprintPlacer extends BaseHUDPart {
createElements(parent) { createElements(parent) {
@ -60,7 +59,7 @@ export class HUDBlueprintPlacer extends BaseHUDPart {
/** /**
* Called when the layer was changed * Called when the layer was changed
* @param {enumLayer} layer * @param {Layer} layer
*/ */
onEditModeChanged(layer) { onEditModeChanged(layer) {
// Check if the layer of the blueprint differs and thus we have to deselect it // Check if the layer of the blueprint differs and thus we have to deselect it

View File

@ -18,7 +18,7 @@ import { THEME } from "../../theme";
import { DynamicDomAttach } from "../dynamic_dom_attach"; import { DynamicDomAttach } from "../dynamic_dom_attach";
import { HUDBuildingPlacerLogic } from "./building_placer_logic"; import { HUDBuildingPlacerLogic } from "./building_placer_logic";
import { makeOffscreenBuffer } from "../../../core/buffer_utils"; import { makeOffscreenBuffer } from "../../../core/buffer_utils";
import { enumLayer } from "../../root"; import { layers } from "../../root";
import { getCodeFromBuildingData } from "../../building_codes"; import { getCodeFromBuildingData } from "../../building_codes";
export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
@ -61,9 +61,9 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
this.currentInterpolatedCornerTile = new Vector(); this.currentInterpolatedCornerTile = new Vector();
this.lockIndicatorSprites = {}; this.lockIndicatorSprites = {};
for (const layerId in enumLayer) { layers.forEach(layer => {
this.lockIndicatorSprites[layerId] = this.makeLockIndicatorSprite(layerId); this.lockIndicatorSprites[layer] = this.makeLockIndicatorSprite(layer);
} });
// //
@ -76,7 +76,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
/** /**
* Makes the lock indicator sprite for the given layer * Makes the lock indicator sprite for the given layer
* @param {enumLayer} layer * @param {Layer} layer
*/ */
makeLockIndicatorSprite(layer) { makeLockIndicatorSprite(layer) {
const dims = 48; const dims = 48;
@ -247,6 +247,31 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
} else { } else {
this.drawRegularPlacement(parameters); this.drawRegularPlacement(parameters);
} }
if (metaBuilding.getShowWiresLayerPreview()) {
this.drawLayerPeek(parameters);
}
}
/**
*
* @param {DrawParameters} parameters
*/
drawLayerPeek(parameters) {
const mousePosition = this.root.app.mousePosition;
if (!mousePosition) {
// Not on screen
return;
}
const worldPosition = this.root.camera.screenToWorld(mousePosition);
// Draw peeker
this.root.hud.parts.layerPreview.renderPreview(
parameters,
worldPosition,
1 / this.root.camera.zoomLevel
);
} }
/** /**
@ -349,7 +374,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
// HACK to draw the entity sprite // HACK to draw the entity sprite
const previewSprite = metaBuilding.getBlueprintSprite(rotationVariant, this.currentVariant.get()); const previewSprite = metaBuilding.getBlueprintSprite(rotationVariant, this.currentVariant.get());
staticComp.origin = worldPos.divideScalar(globalConfig.tileSize).subScalars(0.5, 0.5); staticComp.origin = worldPos.divideScalar(globalConfig.tileSize).subScalars(0.5, 0.5);
staticComp.drawSpriteOnFullEntityBounds(parameters, previewSprite); staticComp.drawSpriteOnBoundsClipped(parameters, previewSprite);
staticComp.origin = mouseTile; staticComp.origin = mouseTile;
// Draw ejectors // Draw ejectors

View File

@ -12,7 +12,6 @@ import { BaseHUDPart } from "../base_hud_part";
import { SOUNDS } from "../../../platform/sound"; import { SOUNDS } from "../../../platform/sound";
import { MetaMinerBuilding, enumMinerVariants } from "../../buildings/miner"; import { MetaMinerBuilding, enumMinerVariants } from "../../buildings/miner";
import { enumHubGoalRewards } from "../../tutorial_goals"; import { enumHubGoalRewards } from "../../tutorial_goals";
import { enumLayer } from "../../root";
import { getBuildingDataFromCode, getCodeFromBuildingData } from "../../building_codes"; import { getBuildingDataFromCode, getCodeFromBuildingData } from "../../building_codes";
import { MetaHubBuilding } from "../../buildings/hub"; import { MetaHubBuilding } from "../../buildings/hub";
@ -133,12 +132,12 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart {
/** /**
* Called when the edit mode got changed * Called when the edit mode got changed
* @param {enumLayer} editMode * @param {Layer} layer
*/ */
onEditModeChanged(editMode) { onEditModeChanged(layer) {
const metaBuilding = this.currentMetaBuilding.get(); const metaBuilding = this.currentMetaBuilding.get();
if (metaBuilding) { if (metaBuilding) {
if (metaBuilding.getLayer() !== editMode) { if (metaBuilding.getLayer() !== layer) {
// This layer doesn't fit the edit mode anymore // This layer doesn't fit the edit mode anymore
this.currentMetaBuilding.set(null); this.currentMetaBuilding.set(null);
} }

View File

@ -8,10 +8,10 @@ import { MetaSplitterBuilding } from "../../buildings/splitter";
import { MetaStackerBuilding } from "../../buildings/stacker"; import { MetaStackerBuilding } from "../../buildings/stacker";
import { MetaTrashBuilding } from "../../buildings/trash"; import { MetaTrashBuilding } from "../../buildings/trash";
import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt"; import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt";
import { enumLayer } from "../../root";
import { HUDBaseToolbar } from "./base_toolbar"; import { HUDBaseToolbar } from "./base_toolbar";
import { MetaLeverBuilding } from "../../buildings/lever"; import { MetaLeverBuilding } from "../../buildings/lever";
import { MetaFilterBuilding } from "../../buildings/filter"; import { MetaFilterBuilding } from "../../buildings/filter";
import { MetaDisplayBuilding } from "../../buildings/display";
const supportedBuildings = [ const supportedBuildings = [
MetaBeltBaseBuilding, MetaBeltBaseBuilding,
@ -26,6 +26,7 @@ const supportedBuildings = [
MetaTrashBuilding, MetaTrashBuilding,
MetaLeverBuilding, MetaLeverBuilding,
MetaFilterBuilding, MetaFilterBuilding,
MetaDisplayBuilding,
]; ];
export class HUDBuildingsToolbar extends HUDBaseToolbar { export class HUDBuildingsToolbar extends HUDBaseToolbar {
@ -33,7 +34,7 @@ export class HUDBuildingsToolbar extends HUDBaseToolbar {
super(root, { super(root, {
supportedBuildings, supportedBuildings,
visibilityCondition: () => visibilityCondition: () =>
!this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === enumLayer.regular, !this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "regular",
htmlElementId: "ingame_HUD_buildings_toolbar", htmlElementId: "ingame_HUD_buildings_toolbar",
}); });
} }

View File

@ -7,8 +7,6 @@ import { DrawParameters } from "../../../core/draw_parameters";
import { THEME } from "../../theme"; import { THEME } from "../../theme";
import { globalConfig } from "../../../core/config"; import { globalConfig } from "../../../core/config";
import { T } from "../../../translations"; import { T } from "../../../translations";
import { enumItemType } from "../../base_item";
import { enumLayer } from "../../root";
export class HUDColorBlindHelper extends BaseHUDPart { export class HUDColorBlindHelper extends BaseHUDPart {
createElements(parent) { createElements(parent) {
@ -41,7 +39,7 @@ export class HUDColorBlindHelper extends BaseHUDPart {
return null; return null;
} }
if (this.root.currentLayer !== enumLayer.regular) { if (this.root.currentLayer !== "regular") {
// Not in regular mode // Not in regular mode
return null; return null;
} }
@ -56,7 +54,7 @@ export class HUDColorBlindHelper extends BaseHUDPart {
// Check if the belt has a color item // Check if the belt has a color item
if (beltComp) { if (beltComp) {
const item = beltComp.assignedPath.findItemAtTile(tile); const item = beltComp.assignedPath.findItemAtTile(tile);
if (item && item.getItemType() === enumItemType.color) { if (item && item.getItemType() === "color") {
return /** @type {ColorItem} */ (item).color; return /** @type {ColorItem} */ (item).color;
} }
} }
@ -66,7 +64,7 @@ export class HUDColorBlindHelper extends BaseHUDPart {
if (ejectorComp) { if (ejectorComp) {
for (let i = 0; i < ejectorComp.slots.length; ++i) { for (let i = 0; i < ejectorComp.slots.length; ++i) {
const slot = ejectorComp.slots[i]; const slot = ejectorComp.slots[i];
if (slot.item && slot.item.getItemType() === enumItemType.color) { if (slot.item && slot.item.getItemType() === "color") {
return /** @type {ColorItem} */ (slot.item).color; return /** @type {ColorItem} */ (slot.item).color;
} }
} }
@ -74,7 +72,7 @@ export class HUDColorBlindHelper extends BaseHUDPart {
} else { } else {
// We hovered a lower layer, show the color there // We hovered a lower layer, show the color there
const lowerLayer = this.root.map.getLowerLayerContentXY(tile.x, tile.y); const lowerLayer = this.root.map.getLowerLayerContentXY(tile.x, tile.y);
if (lowerLayer && lowerLayer.getItemType() === enumItemType.color) { if (lowerLayer && lowerLayer.getItemType() === "color") {
return /** @type {ColorItem} */ (lowerLayer).color; return /** @type {ColorItem} */ (lowerLayer).color;
} }
} }

View File

@ -57,7 +57,7 @@ export class HUDChangesDebugger extends BaseHUDPart {
for (let i = 0; i < this.changes.length; ++i) { for (let i = 0; i < this.changes.length; ++i) {
const change = this.changes[i]; const change = this.changes[i];
parameters.context.fillStyle = change.fillColor; parameters.context.fillStyle = change.fillColor;
parameters.context.globalAlpha = 0.5; parameters.context.globalAlpha = 0.2;
parameters.context.fillRect( parameters.context.fillRect(
change.area.x * globalConfig.tileSize, change.area.x * globalConfig.tileSize,
change.area.y * globalConfig.tileSize, change.area.y * globalConfig.tileSize,

View File

@ -0,0 +1,123 @@
import { freeCanvas, makeOffscreenBuffer } from "../../../core/buffer_utils";
import { globalConfig } from "../../../core/config";
import { Loader } from "../../../core/loader";
import { Vector } from "../../../core/vector";
import { MapChunkView } from "../../map_chunk_view";
import { THEME } from "../../theme";
import { BaseHUDPart } from "../base_hud_part";
/**
* Helper class which allows peaking through to the wires layer
*/
export class HUDLayerPreview extends BaseHUDPart {
initialize() {
this.initializeCanvas();
this.root.signals.aboutToDestruct.add(() => freeCanvas(this.canvas));
this.root.signals.resized.add(this.initializeCanvas, this);
this.previewOverlay = Loader.getSprite("sprites/wires/wires_preview.png");
}
/**
* (re) initializes the canvas
*/
initializeCanvas() {
if (this.canvas) {
freeCanvas(this.canvas);
delete this.canvas;
delete this.context;
}
// Compute how big the preview should be
this.previewSize = Math.round(
Math.min(1024, Math.min(this.root.gameWidth, this.root.gameHeight) * 0.8)
);
const [canvas, context] = makeOffscreenBuffer(this.previewSize, this.previewSize, {
smooth: true,
label: "layerPeeker",
reusable: true,
});
context.clearRect(0, 0, this.previewSize, this.previewSize);
this.canvas = canvas;
this.context = context;
}
/**
* Prepares the canvas to render at the given worldPos and the given camera scale
*
* @param {Vector} worldPos
* @param {number} scale 1 / zoomLevel
*/
prepareCanvasForPreview(worldPos, scale) {
this.context.clearRect(0, 0, this.previewSize, this.previewSize);
this.context.fillStyle = THEME.map.wires.previewColor;
this.context.fillRect(0, 0, this.previewSize, this.previewSize);
const dimensions = scale * this.previewSize;
const startWorldX = worldPos.x - dimensions / 2;
const startWorldY = worldPos.y - dimensions / 2;
const startTileX = Math.floor(startWorldX / globalConfig.tileSize);
const startTileY = Math.floor(startWorldY / globalConfig.tileSize);
const tileDimensions = Math.ceil(dimensions / globalConfig.tileSize);
this.context.save();
this.context.scale(1 / scale, 1 / scale);
this.context.translate(
startTileX * globalConfig.tileSize - startWorldX,
startTileY * globalConfig.tileSize - startWorldY
);
for (let dx = 0; dx < tileDimensions; ++dx) {
for (let dy = 0; dy < tileDimensions; ++dy) {
const tileX = dx + startTileX;
const tileY = dy + startTileY;
const content = this.root.map.getLayerContentXY(tileX, tileY, "wires");
if (content) {
MapChunkView.drawSingleWiresOverviewTile({
context: this.context,
x: dx * globalConfig.tileSize,
y: dy * globalConfig.tileSize,
entity: content,
tileSizePixels: globalConfig.tileSize,
});
}
}
}
this.context.restore();
this.context.globalCompositeOperation = "destination-in";
this.previewOverlay.draw(this.context, 0, 0, this.previewSize, this.previewSize);
this.context.globalCompositeOperation = "source-over";
return this.canvas;
}
/**
* Renders the preview at the given position
* @param {import("../../../core/draw_utils").DrawParameters} parameters
* @param {Vector} worldPos
* @param {number} scale 1 / zoomLevel
*/
renderPreview(parameters, worldPos, scale) {
if (this.root.currentLayer !== "regular") {
// Only supporting wires right now
return;
}
const canvas = this.prepareCanvasForPreview(worldPos, scale);
parameters.context.globalAlpha = 0.3;
parameters.context.drawImage(
canvas,
worldPos.x - (scale * this.previewSize) / 2,
worldPos.y - (scale * this.previewSize) / 2,
scale * this.previewSize,
scale * this.previewSize
);
parameters.context.globalAlpha = 1;
}
}

View File

@ -1,7 +1,6 @@
import { STOP_PROPAGATION } from "../../../core/signal"; import { STOP_PROPAGATION } from "../../../core/signal";
import { Vector } from "../../../core/vector"; import { Vector } from "../../../core/vector";
import { enumMouseButton } from "../../camera"; import { enumMouseButton } from "../../camera";
import { enumLayer } from "../../root";
import { BaseHUDPart } from "../base_hud_part"; import { BaseHUDPart } from "../base_hud_part";
export class HUDLeverToggle extends BaseHUDPart { export class HUDLeverToggle extends BaseHUDPart {
@ -15,7 +14,7 @@ export class HUDLeverToggle extends BaseHUDPart {
*/ */
downPreHandler(pos, button) { downPreHandler(pos, button) {
const tile = this.root.camera.screenToWorld(pos).toTileSpace(); const tile = this.root.camera.screenToWorld(pos).toTileSpace();
const contents = this.root.map.getLayerContentXY(tile.x, tile.y, enumLayer.regular); const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular");
if (contents) { if (contents) {
const leverComp = contents.components.Lever; const leverComp = contents.components.Lever;
if (leverComp) { if (leverComp) {

View File

@ -78,7 +78,7 @@ export class HUDSandboxController extends BaseHUDPart {
if (!this.root.hubGoals.storedShapes[blueprintShape]) { if (!this.root.hubGoals.storedShapes[blueprintShape]) {
this.root.hubGoals.storedShapes[blueprintShape] = 0; this.root.hubGoals.storedShapes[blueprintShape] = 0;
} }
this.root.hubGoals.storedShapes[blueprintShape] += 1e4; this.root.hubGoals.storedShapes[blueprintShape] += 1e9;
} }
maxOutAll() { maxOutAll() {

View File

@ -1,16 +1,22 @@
import { BaseHUDPart } from "../base_hud_part";
import { enumLayer } from "../../root";
import { globalConfig } from "../../../core/config"; import { globalConfig } from "../../../core/config";
import { MapChunkView } from "../../map_chunk_view";
import { WireNetwork } from "../../systems/wire";
import { THEME } from "../../theme";
import { BaseHUDPart } from "../base_hud_part";
import { Loader } from "../../../core/loader";
export class HUDWireInfo extends BaseHUDPart { export class HUDWireInfo extends BaseHUDPart {
initialize() {} initialize() {
this.spriteEmpty = Loader.getSprite("sprites/wires/network_empty.png");
this.spriteConflict = Loader.getSprite("sprites/wires/network_conflict.png");
}
/** /**
* *
* @param {import("../../../core/draw_utils").DrawParameters} parameters * @param {import("../../../core/draw_utils").DrawParameters} parameters
*/ */
drawOverlays(parameters) { drawOverlays(parameters) {
if (this.root.currentLayer !== enumLayer.wires) { if (this.root.currentLayer !== "wires") {
// Not in the wires layer // Not in the wires layer
return; return;
} }
@ -21,38 +27,93 @@ export class HUDWireInfo extends BaseHUDPart {
return; return;
} }
const tile = this.root.camera.screenToWorld(mousePos).toTileSpace(); const worldPos = this.root.camera.screenToWorld(mousePos);
const entity = this.root.map.getLayerContentXY(tile.x, tile.y, enumLayer.wires); const tile = worldPos.toTileSpace();
const entity = this.root.map.getLayerContentXY(tile.x, tile.y, "wires");
if (entity) { if (!entity) {
const wireComp = entity.components.Wire; // No entity
if (wireComp) { return;
const screenTile = this.root.camera.worldToScreen(tile.toWorldSpace()); }
parameters.context.fillStyle = "rgba(0, 0, 0, 0.1)";
parameters.context.fillRect( if (
screenTile.x, !this.root.camera.getIsMapOverlayActive() &&
screenTile.y, !this.root.logic.getIsEntityIntersectedWithMatrix(entity, worldPos)
globalConfig.tileSize * this.root.camera.zoomLevel, ) {
globalConfig.tileSize * this.root.camera.zoomLevel // Detailed intersection check
); return;
}
const networks = this.root.logic.getEntityWireNetworks(entity, tile);
if (networks === null) {
// This entity will never be able to be connected
return;
}
if (networks.length === 0) {
// No network at all
return;
}
for (let i = 0; i < networks.length; ++i) {
const network = networks[i];
this.drawHighlightedNetwork(parameters, network);
}
if (networks.length === 1) {
const network = networks[0];
parameters.context.font = "25px GameFont";
const network = wireComp.linkedNetwork;
if (!network) {
parameters.context.fillStyle = "#333";
parameters.context.fillText("empty", mousePos.x, mousePos.y);
} else {
if (network.valueConflict) { if (network.valueConflict) {
parameters.context.fillStyle = "#a10"; this.spriteConflict.draw(parameters.context, mousePos.x + 15, mousePos.y - 10, 60, 60);
parameters.context.fillText("conflict", mousePos.x, mousePos.y);
} else if (!network.currentValue) { } else if (!network.currentValue) {
parameters.context.fillStyle = "#333"; this.spriteEmpty.draw(parameters.context, mousePos.x + 15, mousePos.y - 10, 60, 60);
parameters.context.fillText("empty", mousePos.x, mousePos.y);
} else { } else {
network.currentValue.draw(mousePos.x + 20, mousePos.y, parameters, 40); network.currentValue.drawItemCenteredClipped(
} mousePos.x + 40,
mousePos.y + 10,
parameters,
60
);
} }
} }
} }
/**
*
*
* @param {import("../../../core/draw_utils").DrawParameters} parameters
* @param {WireNetwork} network
*/
drawHighlightedNetwork(parameters, network) {
parameters.context.globalAlpha = 0.5;
for (let i = 0; i < network.wires.length; ++i) {
const wire = network.wires[i];
const staticComp = wire.components.StaticMapEntity;
const screenTile = this.root.camera.worldToScreen(staticComp.origin.toWorldSpace());
MapChunkView.drawSingleWiresOverviewTile({
context: parameters.context,
x: screenTile.x,
y: screenTile.y,
entity: wire,
tileSizePixels: globalConfig.tileSize * this.root.camera.zoomLevel,
overrideColor: THEME.map.wires.highlightColor,
});
}
for (let i = 0; i < network.tunnels.length; ++i) {
const tunnel = network.tunnels[i];
const staticComp = tunnel.components.StaticMapEntity;
const screenTile = this.root.camera.worldToScreen(staticComp.origin.toWorldSpace());
MapChunkView.drawSingleWiresOverviewTile({
context: parameters.context,
x: screenTile.x,
y: screenTile.y,
entity: tunnel,
tileSizePixels: globalConfig.tileSize * this.root.camera.zoomLevel,
overrideColor: THEME.map.wires.highlightColor,
});
}
parameters.context.globalAlpha = 1;
} }
} }

View File

@ -2,7 +2,6 @@ import { makeOffscreenBuffer } from "../../../core/buffer_utils";
import { globalConfig } from "../../../core/config"; import { globalConfig } from "../../../core/config";
import { DrawParameters } from "../../../core/draw_parameters"; import { DrawParameters } from "../../../core/draw_parameters";
import { KEYMAPPINGS } from "../../key_action_mapper"; import { KEYMAPPINGS } from "../../key_action_mapper";
import { enumLayer } from "../../root";
import { THEME } from "../../theme"; import { THEME } from "../../theme";
import { BaseHUDPart } from "../base_hud_part"; import { BaseHUDPart } from "../base_hud_part";
import { Loader } from "../../../core/loader"; import { Loader } from "../../../core/loader";
@ -26,10 +25,10 @@ export class HUDWiresOverlay extends BaseHUDPart {
* Switches between layers * Switches between layers
*/ */
switchLayers() { switchLayers() {
if (this.root.currentLayer === enumLayer.regular) { if (this.root.currentLayer === "regular") {
this.root.currentLayer = enumLayer.wires; this.root.currentLayer = "wires";
} else { } else {
this.root.currentLayer = enumLayer.regular; this.root.currentLayer = "regular";
} }
this.root.signals.editModeChanged.dispatch(this.root.currentLayer); this.root.signals.editModeChanged.dispatch(this.root.currentLayer);
} }
@ -51,7 +50,7 @@ export class HUDWiresOverlay extends BaseHUDPart {
} }
update() { update() {
const desiredAlpha = this.root.currentLayer === enumLayer.wires ? 1.0 : 0.0; const desiredAlpha = this.root.currentLayer === "wires" ? 1.0 : 0.0;
this.currentAlpha = lerp(this.currentAlpha, desiredAlpha, 0.12); this.currentAlpha = lerp(this.currentAlpha, desiredAlpha, 0.12);
} }

View File

@ -1,4 +1,3 @@
import { enumLayer } from "../../root";
import { HUDBaseToolbar } from "./base_toolbar"; import { HUDBaseToolbar } from "./base_toolbar";
import { MetaWireBuilding } from "../../buildings/wire"; import { MetaWireBuilding } from "../../buildings/wire";
import { MetaConstantSignalBuilding } from "../../buildings/constant_signal"; import { MetaConstantSignalBuilding } from "../../buildings/constant_signal";
@ -19,7 +18,7 @@ export class HUDWiresToolbar extends HUDBaseToolbar {
super(root, { super(root, {
supportedBuildings, supportedBuildings,
visibilityCondition: () => visibilityCondition: () =>
!this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === enumLayer.wires, !this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "wires",
htmlElementId: "ingame_HUD_wires_toolbar", htmlElementId: "ingame_HUD_wires_toolbar",
}); });
} }

View File

@ -1,4 +1,4 @@
import { GameRoot, enumLayer } from "../root"; import { GameRoot } from "../root";
import { globalConfig } from "../../core/config"; import { globalConfig } from "../../core/config";
import { Vector, mixVector } from "../../core/vector"; import { Vector, mixVector } from "../../core/vector";
import { lerp } from "../../core/utils"; import { lerp } from "../../core/utils";

View File

@ -1,7 +1,8 @@
import { DrawParameters } from "../../core/draw_parameters"; import { DrawParameters } from "../../core/draw_parameters";
import { Loader } from "../../core/loader"; import { Loader } from "../../core/loader";
import { types } from "../../savegame/serialization"; import { types } from "../../savegame/serialization";
import { BaseItem, enumItemType } from "../base_item"; import { BaseItem } from "../base_item";
import { globalConfig } from "../../core/config";
export class BooleanItem extends BaseItem { export class BooleanItem extends BaseItem {
static getId() { static getId() {
@ -20,8 +21,9 @@ export class BooleanItem extends BaseItem {
this.value = data; this.value = data;
} }
/** @returns {"boolean"} **/
getItemType() { getItemType() {
return enumItemType.boolean; return "boolean";
} }
/** /**
@ -42,17 +44,17 @@ export class BooleanItem extends BaseItem {
/** /**
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @param {number} size * @param {number} diameter
* @param {DrawParameters} parameters * @param {DrawParameters} parameters
*/ */
draw(x, y, parameters, size = 12) { drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
let sprite; let sprite;
if (this.value) { if (this.value) {
sprite = Loader.getSprite("sprites/wires/boolean_true.png"); sprite = Loader.getSprite("sprites/wires/boolean_true.png");
} else { } else {
sprite = Loader.getSprite("sprites/wires/boolean_false.png"); sprite = Loader.getSprite("sprites/wires/boolean_false.png");
} }
sprite.drawCachedCentered(parameters, x, y, size * 1.5); sprite.drawCachedCentered(parameters, x, y, diameter);
} }
} }

View File

@ -2,9 +2,10 @@ import { globalConfig } from "../../core/config";
import { smoothenDpi } from "../../core/dpi_manager"; import { smoothenDpi } from "../../core/dpi_manager";
import { DrawParameters } from "../../core/draw_parameters"; import { DrawParameters } from "../../core/draw_parameters";
import { types } from "../../savegame/serialization"; import { types } from "../../savegame/serialization";
import { BaseItem, enumItemType } from "../base_item"; import { BaseItem } from "../base_item";
import { enumColors, enumColorsToHexCode } from "../colors"; import { enumColors, enumColorsToHexCode } from "../colors";
import { THEME } from "../theme"; import { THEME } from "../theme";
import { drawSpriteClipped } from "../../core/draw_utils";
export class ColorItem extends BaseItem { export class ColorItem extends BaseItem {
static getId() { static getId() {
@ -23,8 +24,9 @@ export class ColorItem extends BaseItem {
this.color = data; this.color = data;
} }
/** @returns {"color"} **/
getItemType() { getItemType() {
return enumItemType.color; return "color";
} }
/** /**
@ -50,26 +52,36 @@ export class ColorItem extends BaseItem {
/** /**
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @param {number} size * @param {number} diameter
* @param {DrawParameters} parameters * @param {DrawParameters} parameters
*/ */
draw(x, y, parameters, size = 12) { drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
if (!this.bufferGenerator) { if (!this.bufferGenerator) {
this.bufferGenerator = this.internalGenerateColorBuffer.bind(this); this.bufferGenerator = this.internalGenerateColorBuffer.bind(this);
} }
const realDiameter = diameter * 0.6;
const dpi = smoothenDpi(globalConfig.shapesSharpness * parameters.zoomLevel); const dpi = smoothenDpi(globalConfig.shapesSharpness * parameters.zoomLevel);
const key = realDiameter + "/" + dpi + "/" + this.color;
const key = size + "/" + dpi;
const canvas = parameters.root.buffers.getForKey({ const canvas = parameters.root.buffers.getForKey({
key, key: "coloritem",
subKey: this.color, subKey: key,
w: size, w: realDiameter,
h: size, h: realDiameter,
dpi, dpi,
redrawMethod: this.bufferGenerator, redrawMethod: this.bufferGenerator,
}); });
parameters.context.drawImage(canvas, x - size / 2, y - size / 2, size, size);
drawSpriteClipped({
parameters,
sprite: canvas,
x: x - realDiameter / 2,
y: y - realDiameter / 2,
w: realDiameter,
h: realDiameter,
originalW: realDiameter * dpi,
originalH: realDiameter * dpi,
});
} }
/** /**
* *

View File

@ -1,8 +1,9 @@
import { DrawParameters } from "../../core/draw_parameters"; import { DrawParameters } from "../../core/draw_parameters";
import { types } from "../../savegame/serialization"; import { types } from "../../savegame/serialization";
import { BaseItem, enumItemType } from "../base_item"; import { BaseItem } from "../base_item";
import { ShapeDefinition } from "../shape_definition"; import { ShapeDefinition } from "../shape_definition";
import { THEME } from "../theme"; import { THEME } from "../theme";
import { globalConfig } from "../../core/config";
export class ShapeItem extends BaseItem { export class ShapeItem extends BaseItem {
static getId() { static getId() {
@ -21,8 +22,9 @@ export class ShapeItem extends BaseItem {
this.definition = ShapeDefinition.fromShortKey(data); this.definition = ShapeDefinition.fromShortKey(data);
} }
/** @returns {"shape"} **/
getItemType() { getItemType() {
return enumItemType.shape; return "shape";
} }
/** /**
@ -52,9 +54,9 @@ export class ShapeItem extends BaseItem {
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @param {DrawParameters} parameters * @param {DrawParameters} parameters
* @param {number=} size * @param {number=} diameter
*/ */
draw(x, y, parameters, size) { drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
this.definition.draw(x, y, parameters, size); this.definition.drawCentered(x, y, parameters, diameter);
} }
} }

View File

@ -55,12 +55,14 @@ export const KEYMAPPINGS = {
painter: { keyCode: key("9") }, painter: { keyCode: key("9") },
trash: { keyCode: key("0") }, trash: { keyCode: key("0") },
lever: { keyCode: key("L") },
filter: { keyCode: key("B") },
display: { keyCode: key("N") },
wire: { keyCode: key("1") }, wire: { keyCode: key("1") },
wire_tunnel: { keyCode: key("2") }, wire_tunnel: { keyCode: key("2") },
constant_signal: { keyCode: key("3") }, constant_signal: { keyCode: key("3") },
logic_gate: { keyCode: key("4") }, logic_gate: { keyCode: key("4") },
lever: { keyCode: key("5") },
filter: { keyCode: key("6") },
}, },
placement: { placement: {

View File

@ -1,23 +1,20 @@
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { STOP_PROPAGATION } from "../core/signal"; import { STOP_PROPAGATION } from "../core/signal";
import { round2Digits } from "../core/utils"; import { round2Digits } from "../core/utils";
import { import { enumDirection, enumDirectionToVector, enumInvertedDirections, Vector } from "../core/vector";
enumDirection, import { getBuildingDataFromCode } from "./building_codes";
enumDirectionToAngle,
enumDirectionToVector,
enumInvertedDirections,
Vector,
} from "../core/vector";
import { Entity } from "./entity"; import { Entity } from "./entity";
import { MetaBuilding } from "./meta_building"; import { MetaBuilding } from "./meta_building";
import { enumLayer, GameRoot } from "./root"; import { GameRoot } from "./root";
import { WireNetwork } from "./systems/wire";
import { globalConfig } from "../core/config";
import { CHUNK_OVERLAY_RES } from "./map_chunk_view";
const logger = createLogger("ingame/logic"); const logger = createLogger("ingame/logic");
/** @enum {number} */ /** @enum {number} */
export const enumWireEdgeFlag = { export const enumWireEdgeFlag = {
empty: 0, empty: 0,
filled: 1,
connected: 2, connected: 2,
}; };
@ -215,15 +212,97 @@ export class GameLogic {
return false; return false;
} }
if (neighbourStatus === enumWireEdgeFlag.filled) {
return true;
}
if (neighbourStatus === enumWireEdgeFlag.connected) { if (neighbourStatus === enumWireEdgeFlag.connected) {
return true; return true;
} }
} }
/**
* Returns all wire networks this entity participates in on the given tile
* @param {Entity} entity
* @param {Vector} tile
* @returns {Array<WireNetwork>|null} Null if the entity is never able to be connected at the given tile
*/
getEntityWireNetworks(entity, tile) {
let canConnectAtAll = false;
/** @type {Set<WireNetwork>} */
const networks = new Set();
const staticComp = entity.components.StaticMapEntity;
const wireComp = entity.components.Wire;
if (wireComp) {
canConnectAtAll = true;
if (wireComp.linkedNetwork) {
networks.add(wireComp.linkedNetwork);
}
}
const tunnelComp = entity.components.WireTunnel;
if (tunnelComp) {
canConnectAtAll = true;
for (let i = 0; i < tunnelComp.linkedNetworks.length; ++i) {
networks.add(tunnelComp.linkedNetworks[i]);
}
}
const pinsComp = entity.components.WiredPins;
if (pinsComp) {
const slots = pinsComp.slots;
for (let i = 0; i < slots.length; ++i) {
const slot = slots[i];
const slotLocalPos = staticComp.localTileToWorld(slot.pos);
if (slotLocalPos.equals(tile)) {
canConnectAtAll = true;
if (slot.linkedNetwork) {
networks.add(slot.linkedNetwork);
}
}
}
}
if (!canConnectAtAll) {
return null;
}
return Array.from(networks);
}
/**
* Returns if the entities tile *and* his overlay matrix is intersected
* @param {Entity} entity
* @param {Vector} worldPos
*/
getIsEntityIntersectedWithMatrix(entity, worldPos) {
const staticComp = entity.components.StaticMapEntity;
const tile = worldPos.toTileSpace();
if (!staticComp.getTileSpaceBounds().containsPoint(tile.x, tile.y)) {
// No intersection at all
return;
}
const data = getBuildingDataFromCode(staticComp.code);
const overlayMatrix = data.metaInstance.getSpecialOverlayRenderMatrix(
staticComp.rotation,
data.rotationVariant,
data.variant,
entity
);
// Always the same
if (!overlayMatrix) {
return true;
}
const localPosition = worldPos
.divideScalar(globalConfig.tileSize)
.modScalar(1)
.multiplyScalar(CHUNK_OVERLAY_RES)
.floor();
return !!overlayMatrix[localPosition.x + localPosition.y * 3];
}
/** /**
* Gets the flag at the given tile * Gets the flag at the given tile
* @param {Vector} tile * @param {Vector} tile
@ -268,15 +347,26 @@ export class GameLogic {
} }
// Now check if there's a connectable wire // Now check if there's a connectable wire
const targetEntity = this.root.map.getTileContent(tile, enumLayer.wires); const targetEntity = this.root.map.getTileContent(tile, "wires");
if (!targetEntity) { if (!targetEntity) {
return enumWireEdgeFlag.empty; return enumWireEdgeFlag.empty;
} }
const targetStaticComp = targetEntity.components.StaticMapEntity;
// Check if its a crossing // Check if its a crossing
const wireTunnelComp = targetEntity.components.WireTunnel; const wireTunnelComp = targetEntity.components.WireTunnel;
if (wireTunnelComp) { if (wireTunnelComp) {
return enumWireEdgeFlag.filled; // Check if the crossing is connected
if (wireTunnelComp.multipleDirections) {
return enumWireEdgeFlag.connected;
} else {
// Its a coating, check if it matches the direction
const referenceDirection = targetStaticComp.localDirectionToWorld(enumDirection.top);
return referenceDirection === edge || enumInvertedDirections[referenceDirection] === edge
? enumWireEdgeFlag.connected
: enumWireEdgeFlag.empty;
}
} }
// Check if its a wire // Check if its a wire
@ -285,15 +375,9 @@ export class GameLogic {
return enumWireEdgeFlag.empty; return enumWireEdgeFlag.empty;
} }
const refAngle = enumDirectionToAngle[edge]; // const refAngle = enumDirectionToAngle[edge];
const refRotation = targetEntity.components.StaticMapEntity.originalRotation; // const refRotation = targetEntity.components.StaticMapEntity.originalRotation;
const canConnectRemotely = refRotation === refAngle || (refRotation + 180) % 360 === refAngle; // const canConnectRemotely = refRotation === refAngle || (refRotation + 180) % 360 === refAngle;
// Check if the wire points towards the right direction
if (!canConnectRemotely) {
// Seems its not the right direction - well, still its filled
return enumWireEdgeFlag.filled;
}
// Actually connected // Actually connected
return enumWireEdgeFlag.connected; return enumWireEdgeFlag.connected;
@ -317,7 +401,7 @@ export class GameLogic {
continue; continue;
} }
const entity = this.root.map.getLayerContentXY(tile.x + dx, tile.y + dy, enumLayer.regular); const entity = this.root.map.getLayerContentXY(tile.x + dx, tile.y + dy, "regular");
if (entity) { if (entity) {
let ejectorSlots = []; let ejectorSlots = [];
let acceptorSlots = []; let acceptorSlots = [];

View File

@ -1,13 +1,10 @@
import { GameRoot, enumLayer } from "./root";
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { Vector } from "../core/vector"; import { Vector } from "../core/vector";
import { Entity } from "./entity";
import { createLogger } from "../core/logging";
import { BaseItem } from "./base_item";
import { MapChunkView } from "./map_chunk_view";
import { BasicSerializableObject, types } from "../savegame/serialization"; import { BasicSerializableObject, types } from "../savegame/serialization";
import { BaseItem } from "./base_item";
const logger = createLogger("map"); import { Entity } from "./entity";
import { MapChunkView } from "./map_chunk_view";
import { GameRoot } from "./root";
export class BaseMap extends BasicSerializableObject { export class BaseMap extends BasicSerializableObject {
static getId() { static getId() {
@ -97,7 +94,7 @@ export class BaseMap extends BasicSerializableObject {
/** /**
* Returns the tile content of a given tile * Returns the tile content of a given tile
* @param {Vector} tile * @param {Vector} tile
* @param {enumLayer} layer * @param {Layer} layer
* @returns {Entity} Entity or null * @returns {Entity} Entity or null
*/ */
getTileContent(tile, layer) { getTileContent(tile, layer) {
@ -122,7 +119,7 @@ export class BaseMap extends BasicSerializableObject {
* Returns the tile content of a given tile * Returns the tile content of a given tile
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @param {enumLayer} layer * @param {Layer} layer
* @returns {Entity} Entity or null * @returns {Entity} Entity or null
*/ */
getLayerContentXY(x, y, layer) { getLayerContentXY(x, y, layer) {
@ -147,7 +144,7 @@ export class BaseMap extends BasicSerializableObject {
/** /**
* Checks if the tile is used * Checks if the tile is used
* @param {Vector} tile * @param {Vector} tile
* @param {enumLayer} layer * @param {Layer} layer
* @returns {boolean} * @returns {boolean}
*/ */
isTileUsed(tile, layer) { isTileUsed(tile, layer) {
@ -162,7 +159,7 @@ export class BaseMap extends BasicSerializableObject {
* Checks if the tile is used * Checks if the tile is used
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @param {enumLayer} layer * @param {Layer} layer
* @returns {boolean} * @returns {boolean}
*/ */
isTileUsedXY(x, y, layer) { isTileUsedXY(x, y, layer) {

View File

@ -7,8 +7,9 @@ import { BaseItem } from "./base_item";
import { enumColors } from "./colors"; import { enumColors } from "./colors";
import { Entity } from "./entity"; import { Entity } from "./entity";
import { COLOR_ITEM_SINGLETONS } from "./items/color_item"; import { COLOR_ITEM_SINGLETONS } from "./items/color_item";
import { enumLayer, GameRoot } from "./root"; import { GameRoot } from "./root";
import { enumSubShape } from "./shape_definition"; import { enumSubShape } from "./shape_definition";
import { Rectangle } from "../core/rectangle";
const logger = createLogger("map_chunk"); const logger = createLogger("map_chunk");
@ -26,25 +27,54 @@ export class MapChunk {
this.tileX = x * globalConfig.mapChunkSize; this.tileX = x * globalConfig.mapChunkSize;
this.tileY = y * globalConfig.mapChunkSize; this.tileY = y * globalConfig.mapChunkSize;
/** @type {Array<Array<?Entity>>} */ /**
* Stores the contents of the lower (= map resources) layer
* @type {Array<Array<?BaseItem>>}
*/
this.lowerLayer = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize);
/**
* Stores the contents of the regular layer
* @type {Array<Array<?Entity>>}
*/
this.contents = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize); this.contents = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize);
/** @type {Array<Array<?Entity>>} */ /**
* Stores the contents of the wires layer
* @type {Array<Array<?Entity>>}
*/
this.wireContents = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize); this.wireContents = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize);
/** @type {Array<Array<?BaseItem>>} */
this.lowerLayer = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize);
/** @type {Array<Entity>} */ /** @type {Array<Entity>} */
this.containedEntities = []; this.containedEntities = [];
/**
* World space rectangle, can be used for culling
*/
this.worldSpaceRectangle = new Rectangle(
this.tileX * globalConfig.tileSize,
this.tileY * globalConfig.tileSize,
globalConfig.mapChunkWorldSize,
globalConfig.mapChunkWorldSize
);
/**
* Tile space rectangle, can be used for culling
*/
this.tileSpaceRectangle = new Rectangle(
this.tileX,
this.tileY,
globalConfig.mapChunkSize,
globalConfig.mapChunkSize
);
/** /**
* Which entities this chunk contains, sorted by layer * Which entities this chunk contains, sorted by layer
* @type {Object<enumLayer, Array<Entity>>} * @type {Record<Layer, Array<Entity>>}
*/ */
this.containedEntitiesByLayer = { this.containedEntitiesByLayer = {
[enumLayer.regular]: [], regular: [],
[enumLayer.wires]: [], wires: [],
}; };
/** /**
@ -332,7 +362,7 @@ export class MapChunk {
* Returns the contents of this chunk from the given world space coordinates * Returns the contents of this chunk from the given world space coordinates
* @param {number} worldX * @param {number} worldX
* @param {number} worldY * @param {number} worldY
* @param {enumLayer} layer * @param {Layer} layer
* @returns {Entity=} * @returns {Entity=}
*/ */
getLayerContentFromWorldCoords(worldX, worldY, layer) { getLayerContentFromWorldCoords(worldX, worldY, layer) {
@ -342,7 +372,7 @@ export class MapChunk {
assert(localY >= 0, "Local Y is < 0"); assert(localY >= 0, "Local Y is < 0");
assert(localX < globalConfig.mapChunkSize, "Local X is >= chunk size"); assert(localX < globalConfig.mapChunkSize, "Local X is >= chunk size");
assert(localY < globalConfig.mapChunkSize, "Local Y is >= chunk size"); assert(localY < globalConfig.mapChunkSize, "Local Y is >= chunk size");
if (layer === enumLayer.regular) { if (layer === "regular") {
return this.contents[localX][localY] || null; return this.contents[localX][localY] || null;
} else { } else {
return this.wireContents[localX][localY] || null; return this.wireContents[localX][localY] || null;
@ -395,7 +425,7 @@ export class MapChunk {
* @param {number} tileX * @param {number} tileX
* @param {number} tileY * @param {number} tileY
* @param {Entity=} contents * @param {Entity=} contents
* @param {enumLayer} layer * @param {Layer} layer
*/ */
setLayerContentFromWorldCords(tileX, tileY, contents, layer) { setLayerContentFromWorldCords(tileX, tileY, contents, layer) {
const localX = tileX - this.tileX; const localX = tileX - this.tileX;
@ -406,7 +436,7 @@ export class MapChunk {
assert(localY < globalConfig.mapChunkSize, "Local Y is >= chunk size"); assert(localY < globalConfig.mapChunkSize, "Local Y is >= chunk size");
let oldContents; let oldContents;
if (layer === enumLayer.regular) { if (layer === "regular") {
oldContents = this.contents[localX][localY]; oldContents = this.contents[localX][localY];
} else { } else {
oldContents = this.wireContents[localX][localY]; oldContents = this.wireContents[localX][localY];
@ -420,7 +450,7 @@ export class MapChunk {
fastArrayDeleteValueIfContained(this.containedEntitiesByLayer[layer], oldContents); fastArrayDeleteValueIfContained(this.containedEntitiesByLayer[layer], oldContents);
} }
if (layer === enumLayer.regular) { if (layer === "regular") {
this.contents[localX][localY] = contents; this.contents[localX][localY] = contents;
} else { } else {
this.wireContents[localX][localY] = contents; this.wireContents[localX][localY] = contents;

View File

@ -1,12 +1,13 @@
import { MapChunk } from "./map_chunk";
import { GameRoot, enumLayer } from "./root";
import { DrawParameters } from "../core/draw_parameters";
import { smoothenDpi } from "../core/dpi_manager";
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { THEME } from "./theme"; import { DrawParameters } from "../core/draw_parameters";
import { getBuildingDataFromCode } from "./building_codes"; import { getBuildingDataFromCode } from "./building_codes";
import { Entity } from "./entity";
import { MapChunk } from "./map_chunk";
import { GameRoot } from "./root";
import { THEME } from "./theme";
import { drawSpriteClipped } from "../core/draw_utils";
const CHUNK_OVERLAY_RES = 3; export const CHUNK_OVERLAY_RES = 3;
export class MapChunkView extends MapChunk { export class MapChunkView extends MapChunk {
/** /**
@ -41,6 +42,7 @@ export class MapChunkView extends MapChunk {
drawBackgroundLayer(parameters) { drawBackgroundLayer(parameters) {
const systems = this.root.systemMgr.systems; const systems = this.root.systemMgr.systems;
systems.mapResources.drawChunk(parameters, this); systems.mapResources.drawChunk(parameters, this);
systems.beltUnderlays.drawChunk(parameters, this);
systems.belt.drawChunk(parameters, this); systems.belt.drawChunk(parameters, this);
} }
@ -50,9 +52,16 @@ export class MapChunkView extends MapChunk {
*/ */
drawForegroundLayer(parameters) { drawForegroundLayer(parameters) {
const systems = this.root.systemMgr.systems; const systems = this.root.systemMgr.systems;
systems.itemEjector.drawChunk(parameters, this);
systems.itemAcceptor.drawChunk(parameters, this);
systems.miner.drawChunk(parameters, this); systems.miner.drawChunk(parameters, this);
systems.staticMapEntities.drawChunk(parameters, this); systems.staticMapEntities.drawChunk(parameters, this);
systems.lever.drawChunk(parameters, this); systems.lever.drawChunk(parameters, this);
systems.display.drawChunk(parameters, this);
systems.storage.drawChunk(parameters, this);
} }
/** /**
@ -60,11 +69,12 @@ export class MapChunkView extends MapChunk {
* @param {DrawParameters} parameters * @param {DrawParameters} parameters
*/ */
drawOverlay(parameters) { drawOverlay(parameters) {
const overlaySize = globalConfig.mapChunkSize * CHUNK_OVERLAY_RES;
const sprite = this.root.buffers.getForKey({ const sprite = this.root.buffers.getForKey({
key: "chunk@" + this.root.currentLayer, key: "chunk@" + this.root.currentLayer,
subKey: this.renderKey, subKey: this.renderKey,
w: globalConfig.mapChunkSize * CHUNK_OVERLAY_RES, w: overlaySize,
h: globalConfig.mapChunkSize * CHUNK_OVERLAY_RES, h: overlaySize,
dpi: 1, dpi: 1,
redrawMethod: this.generateOverlayBuffer.bind(this), redrawMethod: this.generateOverlayBuffer.bind(this),
}); });
@ -73,20 +83,29 @@ export class MapChunkView extends MapChunk {
// Draw chunk "pixel" art // Draw chunk "pixel" art
parameters.context.imageSmoothingEnabled = false; parameters.context.imageSmoothingEnabled = false;
parameters.context.drawImage(sprite, this.x * dims, this.y * dims, dims, dims); drawSpriteClipped({
parameters,
sprite,
x: this.x * dims,
y: this.y * dims,
w: dims,
h: dims,
originalW: overlaySize,
originalH: overlaySize,
});
parameters.context.imageSmoothingEnabled = true; parameters.context.imageSmoothingEnabled = true;
// Draw patch items // Draw patch items
if (this.root.currentLayer === enumLayer.regular) { if (this.root.currentLayer === "regular") {
for (let i = 0; i < this.patches.length; ++i) { for (let i = 0; i < this.patches.length; ++i) {
const patch = this.patches[i]; const patch = this.patches[i];
patch.item.draw( const destX = this.x * dims + patch.pos.x * globalConfig.tileSize;
this.x * dims + patch.pos.x * globalConfig.tileSize, const destY = this.y * dims + patch.pos.y * globalConfig.tileSize;
this.y * dims + patch.pos.y * globalConfig.tileSize, const diameter = Math.min(80, 30 / parameters.zoomLevel);
parameters,
Math.min(80, 30 / parameters.zoomLevel) patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter);
);
} }
} }
} }
@ -178,7 +197,7 @@ export class MapChunkView extends MapChunk {
} }
} }
if (this.root.currentLayer === enumLayer.wires) { if (this.root.currentLayer === "wires") {
// Draw wires overlay // Draw wires overlay
context.fillStyle = THEME.map.wires.overlayColor; context.fillStyle = THEME.map.wires.overlayColor;
@ -191,46 +210,54 @@ export class MapChunkView extends MapChunk {
if (!content) { if (!content) {
continue; continue;
} }
const staticComp = content.components.StaticMapEntity; MapChunkView.drawSingleWiresOverviewTile({
context,
x: x * CHUNK_OVERLAY_RES,
y: y * CHUNK_OVERLAY_RES,
entity: content,
tileSizePixels: CHUNK_OVERLAY_RES,
});
}
}
}
}
/**
* @param {object} param0
* @param {CanvasRenderingContext2D} param0.context
* @param {number} param0.x
* @param {number} param0.y
* @param {Entity} param0.entity
* @param {number} param0.tileSizePixels
* @param {string=} param0.overrideColor Optionally override the color to be rendered
*/
static drawSingleWiresOverviewTile({ context, x, y, entity, tileSizePixels, overrideColor = null }) {
const staticComp = entity.components.StaticMapEntity;
const data = getBuildingDataFromCode(staticComp.code); const data = getBuildingDataFromCode(staticComp.code);
const metaBuilding = data.metaInstance; const metaBuilding = data.metaInstance;
const overlayMatrix = metaBuilding.getSpecialOverlayRenderMatrix( const overlayMatrix = metaBuilding.getSpecialOverlayRenderMatrix(
staticComp.rotation, staticComp.rotation,
data.rotationVariant, data.rotationVariant,
data.variant, data.variant,
content entity
); );
context.fillStyle = overrideColor || metaBuilding.getSilhouetteColor();
context.fillStyle = metaBuilding.getSilhouetteColor();
if (overlayMatrix) { if (overlayMatrix) {
for (let dx = 0; dx < 3; ++dx) { for (let dx = 0; dx < 3; ++dx) {
for (let dy = 0; dy < 3; ++dy) { for (let dy = 0; dy < 3; ++dy) {
const isFilled = overlayMatrix[dx + dy * 3]; const isFilled = overlayMatrix[dx + dy * 3];
if (isFilled) { if (isFilled) {
context.fillRect( context.fillRect(
x * CHUNK_OVERLAY_RES + dx, x + (dx * tileSizePixels) / CHUNK_OVERLAY_RES,
y * CHUNK_OVERLAY_RES + dy, y + (dy * tileSizePixels) / CHUNK_OVERLAY_RES,
1, tileSizePixels / CHUNK_OVERLAY_RES,
1 tileSizePixels / CHUNK_OVERLAY_RES
); );
} }
} }
} }
continue;
} else { } else {
context.fillRect( context.fillRect(x, y, tileSizePixels, tileSizePixels);
x * CHUNK_OVERLAY_RES,
y * CHUNK_OVERLAY_RES,
CHUNK_OVERLAY_RES,
CHUNK_OVERLAY_RES
);
continue;
}
}
}
} }
} }
@ -242,5 +269,6 @@ export class MapChunkView extends MapChunk {
const systems = this.root.systemMgr.systems; const systems = this.root.systemMgr.systems;
systems.wire.drawChunk(parameters, this); systems.wire.drawChunk(parameters, this);
systems.staticMapEntities.drawWiresChunk(parameters, this); systems.staticMapEntities.drawWiresChunk(parameters, this);
systems.wiredPins.drawChunk(parameters, this);
} }
} }

View File

@ -140,23 +140,23 @@ export class MapView extends BaseMap {
* @param {function} method * @param {function} method
*/ */
drawVisibleChunks(parameters, method) { drawVisibleChunks(parameters, method) {
const cullRange = parameters.visibleRect.toTileCullRectangle(); const cullRange = parameters.visibleRect.allScaled(1 / globalConfig.tileSize);
const top = cullRange.top(); const top = cullRange.top();
const right = cullRange.right(); const right = cullRange.right();
const bottom = cullRange.bottom(); const bottom = cullRange.bottom();
const left = cullRange.left(); const left = cullRange.left();
const border = 1; const border = 0;
const minY = top - border; const minY = top - border;
const maxY = bottom + border; const maxY = bottom + border;
const minX = left - border; const minX = left - border;
const maxX = right + border - 1; const maxX = right + border;
const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize); const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize);
const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize); const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize);
const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize); const chunkEndX = Math.floor(maxX / globalConfig.mapChunkSize);
const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize); const chunkEndY = Math.floor(maxY / globalConfig.mapChunkSize);
// Render y from top down for proper blending // Render y from top down for proper blending
for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) { for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) {
@ -230,7 +230,6 @@ export class MapView extends BaseMap {
const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize); const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize);
const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize); const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize);
// Render y from top down for proper blending
for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) { for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) {
for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) { for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) {
parameters.context.fillStyle = "#ffaaaa"; parameters.context.fillStyle = "#ffaaaa";

View File

@ -4,7 +4,7 @@ import { Vector } from "../core/vector";
import { SOUNDS } from "../platform/sound"; import { SOUNDS } from "../platform/sound";
import { StaticMapEntityComponent } from "./components/static_map_entity"; import { StaticMapEntityComponent } from "./components/static_map_entity";
import { Entity } from "./entity"; import { Entity } from "./entity";
import { enumLayer, GameRoot } from "./root"; import { GameRoot } from "./root";
import { getCodeFromBuildingData } from "./building_codes"; import { getCodeFromBuildingData } from "./building_codes";
export const defaultBuildingVariant = "default"; export const defaultBuildingVariant = "default";
@ -27,10 +27,10 @@ export class MetaBuilding {
/** /**
* Returns the edit layer of the building * Returns the edit layer of the building
* @returns {enumLayer} * @returns {Layer}
*/ */
getLayer() { getLayer() {
return enumLayer.regular; return "regular";
} }
/** /**
@ -91,6 +91,13 @@ export class MetaBuilding {
return false; return false;
} }
/**
* Whether to show a preview of the wires layer when placing the building
*/
getShowWiresLayerPreview() {
return false;
}
/** /**
* Whether to rotate automatically in the dragging direction while placing * Whether to rotate automatically in the dragging direction while placing
* @param {string} variant * @param {string} variant
@ -229,7 +236,7 @@ export class MetaBuilding {
* @param {Vector} param0.tile * @param {Vector} param0.tile
* @param {number} param0.rotation * @param {number} param0.rotation
* @param {string} param0.variant * @param {string} param0.variant
* @param {string} param0.layer * @param {Layer} param0.layer
* @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array<Entity> }} * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array<Entity> }}
*/ */
computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) { computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) {

View File

@ -19,7 +19,8 @@ import { MetaConstantSignalBuilding } from "./buildings/constant_signal";
import { MetaLogicGateBuilding, enumLogicGateVariants } from "./buildings/logic_gate"; import { MetaLogicGateBuilding, enumLogicGateVariants } from "./buildings/logic_gate";
import { MetaLeverBuilding } from "./buildings/lever"; import { MetaLeverBuilding } from "./buildings/lever";
import { MetaFilterBuilding } from "./buildings/filter"; import { MetaFilterBuilding } from "./buildings/filter";
import { MetaWireTunnelBuilding } from "./buildings/wire_tunnel"; import { MetaWireTunnelBuilding, enumWireTunnelVariants } from "./buildings/wire_tunnel";
import { MetaDisplayBuilding } from "./buildings/display";
const logger = createLogger("building_registry"); const logger = createLogger("building_registry");
@ -41,6 +42,7 @@ export function initMetaBuildingRegistry() {
gMetaBuildingRegistry.register(MetaLeverBuilding); gMetaBuildingRegistry.register(MetaLeverBuilding);
gMetaBuildingRegistry.register(MetaFilterBuilding); gMetaBuildingRegistry.register(MetaFilterBuilding);
gMetaBuildingRegistry.register(MetaWireTunnelBuilding); gMetaBuildingRegistry.register(MetaWireTunnelBuilding);
gMetaBuildingRegistry.register(MetaDisplayBuilding);
// Belt // Belt
registerBuildingVariant(1, MetaBeltBaseBuilding, defaultBuildingVariant, 0); registerBuildingVariant(1, MetaBeltBaseBuilding, defaultBuildingVariant, 0);
@ -114,6 +116,10 @@ export function initMetaBuildingRegistry() {
// Wire tunnel // Wire tunnel
registerBuildingVariant(39, MetaWireTunnelBuilding); registerBuildingVariant(39, MetaWireTunnelBuilding);
registerBuildingVariant(41, MetaWireTunnelBuilding, enumWireTunnelVariants.coating);
// Display
registerBuildingVariant(40, MetaDisplayBuilding);
// Propagate instances // Propagate instances
for (const key in gBuildingVariants) { for (const key in gBuildingVariants) {

View File

@ -1,7 +1,7 @@
import { GameRoot } from "./root"; import { GameRoot } from "./root";
import { ShapeDefinition } from "./shape_definition"; import { ShapeDefinition } from "./shape_definition";
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { BaseItem, enumItemType } from "./base_item"; import { BaseItem } from "./base_item";
import { ShapeItem } from "./items/shape_item"; import { ShapeItem } from "./items/shape_item";
import { BasicSerializableObject } from "../savegame/serialization"; import { BasicSerializableObject } from "../savegame/serialization";
@ -53,7 +53,7 @@ export class ProductionAnalytics extends BasicSerializableObject {
* @param {BaseItem} item * @param {BaseItem} item
*/ */
onItemProduced(item) { onItemProduced(item) {
if (item.getItemType() === enumItemType.shape) { if (item.getItemType() === "shape") {
const definition = /** @type {ShapeItem} */ (item).definition; const definition = /** @type {ShapeItem} */ (item).definition;
const key = definition.getHash(); const key = definition.getHash();
const entry = this.history[enumAnalyticsDataSource.produced]; const entry = this.history[enumAnalyticsDataSource.produced];

View File

@ -1,5 +1,4 @@
/* eslint-disable no-unused-vars */ /* eslint-disable no-unused-vars */
import { Signal } from "../core/signal"; import { Signal } from "../core/signal";
import { RandomNumberGenerator } from "../core/rng"; import { RandomNumberGenerator } from "../core/rng";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
@ -32,14 +31,8 @@ import { Vector } from "../core/vector";
const logger = createLogger("game/root"); const logger = createLogger("game/root");
/** @enum {string} */ /** @type {Array<Layer>} */
export const enumLayer = { export const layers = ["regular", "wires"];
regular: "regular",
wires: "wires",
};
/** @type {Array<enumLayer>} */
export const arrayLayers = [enumLayer.regular, enumLayer.wires];
/** /**
* The game root is basically the whole game state at a given point, * The game root is basically the whole game state at a given point,
@ -134,8 +127,8 @@ export class GameRoot {
/** @type {DynamicTickrate} */ /** @type {DynamicTickrate} */
this.dynamicTickrate = null; this.dynamicTickrate = null;
/** @type {enumLayer} */ /** @type {Layer} */
this.currentLayer = enumLayer.regular; this.currentLayer = "regular";
this.signals = { this.signals = {
// Entities // Entities
@ -156,6 +149,8 @@ export class GameRoot {
gameSaved: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got saved gameSaved: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got saved
gameRestored: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got restored gameRestored: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got restored
gameFrameStarted: /** @type {TypedSignal<[]>} */ (new Signal()), // New frame
storyGoalCompleted: /** @type {TypedSignal<[number, string]>} */ (new Signal()), storyGoalCompleted: /** @type {TypedSignal<[number, string]>} */ (new Signal()),
upgradePurchased: /** @type {TypedSignal<[string]>} */ (new Signal()), upgradePurchased: /** @type {TypedSignal<[string]>} */ (new Signal()),
@ -167,7 +162,7 @@ export class GameRoot {
bulkOperationFinished: /** @type {TypedSignal<[]>} */ (new Signal()), bulkOperationFinished: /** @type {TypedSignal<[]>} */ (new Signal()),
editModeChanged: /** @type {TypedSignal<[enumLayer]>} */ (new Signal()), editModeChanged: /** @type {TypedSignal<[Layer]>} */ (new Signal()),
// Called to check if an entity can be placed, second parameter is an additional offset. // Called to check if an entity can be placed, second parameter is an additional offset.
// Use to introduce additional placement checks // Use to introduce additional placement checks

View File

@ -2,16 +2,11 @@ import { makeOffscreenBuffer } from "../core/buffer_utils";
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { smoothenDpi } from "../core/dpi_manager"; import { smoothenDpi } from "../core/dpi_manager";
import { DrawParameters } from "../core/draw_parameters"; import { DrawParameters } from "../core/draw_parameters";
import { createLogger } from "../core/logging";
import { Vector } from "../core/vector"; import { Vector } from "../core/vector";
import { BasicSerializableObject, types } from "../savegame/serialization"; import { BasicSerializableObject, types } from "../savegame/serialization";
import { enumColors, enumColorsToHexCode, enumColorToShortcode, enumShortcodeToColor } from "./colors"; import { enumColors, enumColorsToHexCode, enumColorToShortcode, enumShortcodeToColor } from "./colors";
import { THEME } from "./theme"; import { THEME } from "./theme";
const rusha = require("rusha");
const logger = createLogger("shape_definition");
/** /**
* @typedef {{ * @typedef {{
* subShape: enumSubShape, * subShape: enumSubShape,
@ -280,24 +275,25 @@ export class ShapeDefinition extends BasicSerializableObject {
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @param {DrawParameters} parameters * @param {DrawParameters} parameters
* @param {number=} diameter
*/ */
draw(x, y, parameters, size = 20) { drawCentered(x, y, parameters, diameter = 20) {
const dpi = smoothenDpi(globalConfig.shapesSharpness * parameters.zoomLevel); const dpi = smoothenDpi(globalConfig.shapesSharpness * parameters.zoomLevel);
if (!this.bufferGenerator) { if (!this.bufferGenerator) {
this.bufferGenerator = this.internalGenerateShapeBuffer.bind(this); this.bufferGenerator = this.internalGenerateShapeBuffer.bind(this);
} }
const key = size + "/" + dpi; const key = diameter + "/" + dpi + "/" + this.cachedHash;
const canvas = parameters.root.buffers.getForKey({ const canvas = parameters.root.buffers.getForKey({
key, key: "shapedef",
subKey: this.cachedHash, subKey: key,
w: size, w: diameter,
h: size, h: diameter,
dpi, dpi,
redrawMethod: this.bufferGenerator, redrawMethod: this.bufferGenerator,
}); });
parameters.context.drawImage(canvas, x - size / 2, y - size / 2, size, size); parameters.context.drawImage(canvas, x - diameter / 2, y - diameter / 2, diameter, diameter);
} }
/** /**

View File

@ -495,17 +495,15 @@ export class BeltSystem extends GameSystemWithFilter {
((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) * ((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) *
globalConfig.itemSpacingOnBelts globalConfig.itemSpacingOnBelts
); );
const contents = chunk.contents; const contents = chunk.containedEntitiesByLayer.regular;
for (let y = 0; y < globalConfig.mapChunkSize; ++y) { for (let i = 0; i < contents.length; ++i) {
for (let x = 0; x < globalConfig.mapChunkSize; ++x) { const entity = contents[i];
const entity = contents[x][y]; if (entity.components.Belt) {
if (entity && entity.components.Belt) {
const direction = entity.components.Belt.direction; const direction = entity.components.Belt.direction;
const sprite = this.beltAnimations[direction][animationIndex % BELT_ANIM_COUNT]; const sprite = this.beltAnimations[direction][animationIndex % BELT_ANIM_COUNT];
entity.components.StaticMapEntity.drawSpriteOnFullEntityBounds(parameters, sprite, 0); // Culling happens within the static map entity component
} entity.components.StaticMapEntity.drawSpriteOnBoundsClipped(parameters, sprite, 0);
} }
} }
} }

View File

@ -3,9 +3,10 @@ import { drawRotatedSprite } from "../../core/draw_utils";
import { Loader } from "../../core/loader"; import { Loader } from "../../core/loader";
import { enumDirectionToAngle } from "../../core/vector"; import { enumDirectionToAngle } from "../../core/vector";
import { BeltUnderlaysComponent } from "../components/belt_underlays"; import { BeltUnderlaysComponent } from "../components/belt_underlays";
import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter"; import { GameSystemWithFilter } from "../game_system_with_filter";
import { BELT_ANIM_COUNT } from "./belt"; import { BELT_ANIM_COUNT } from "./belt";
import { MapChunkView } from "../map_chunk_view";
import { DrawParameters } from "../../core/draw_parameters";
export class BeltUnderlaysSystem extends GameSystemWithFilter { export class BeltUnderlaysSystem extends GameSystemWithFilter {
constructor(root) { constructor(root) {
@ -19,33 +20,48 @@ export class BeltUnderlaysSystem extends GameSystemWithFilter {
} }
/** /**
* Draws the acceptor underlays * Draws a given chunk
* @param {import("../../core/draw_utils").DrawParameters} parameters * @param {DrawParameters} parameters
* @param {MapChunkView} chunk
*/ */
drawUnderlays(parameters) { drawChunk(parameters, chunk) {
this.forEachMatchingEntityOnScreen(parameters, this.drawEntityUnderlays.bind(this));
}
/**
* @param {import("../../core/draw_utils").DrawParameters} parameters
* @param {Entity} entity
*/
drawEntityUnderlays(parameters, entity) {
const staticComp = entity.components.StaticMapEntity;
const underlayComp = entity.components.BeltUnderlays;
if (!staticComp.shouldBeDrawn(parameters)) {
return;
}
// Limit speed to avoid belts going backwards // Limit speed to avoid belts going backwards
const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(), 10); const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(), 10);
const contents = chunk.containedEntitiesByLayer.regular;
for (let i = 0; i < contents.length; ++i) {
const entity = contents[i];
const underlayComp = entity.components.BeltUnderlays;
if (!underlayComp) {
continue;
}
const staticComp = entity.components.StaticMapEntity;
const underlays = underlayComp.underlays; const underlays = underlayComp.underlays;
for (let i = 0; i < underlays.length; ++i) { for (let i = 0; i < underlays.length; ++i) {
const { pos, direction } = underlays[i]; const { pos, direction } = underlays[i];
const transformedPos = staticComp.localTileToWorld(pos); const transformedPos = staticComp.localTileToWorld(pos);
// Culling
if (!chunk.tileSpaceRectangle.containsPoint(transformedPos.x, transformedPos.y)) {
continue;
}
const destX = transformedPos.x * globalConfig.tileSize;
const destY = transformedPos.y * globalConfig.tileSize;
// Culling, #2
if (
!parameters.visibleRect.containsRect4Params(
destX,
destY,
globalConfig.tileSize,
globalConfig.tileSize
)
) {
continue;
}
const angle = enumDirectionToAngle[staticComp.localDirectionToWorld(direction)]; const angle = enumDirectionToAngle[staticComp.localDirectionToWorld(direction)];
// SYNC with systems/belt.js:drawSingleEntity! // SYNC with systems/belt.js:drawSingleEntity!
@ -57,11 +73,12 @@ export class BeltUnderlaysSystem extends GameSystemWithFilter {
drawRotatedSprite({ drawRotatedSprite({
parameters, parameters,
sprite: this.underlayBeltSprites[animationIndex % this.underlayBeltSprites.length], sprite: this.underlayBeltSprites[animationIndex % this.underlayBeltSprites.length],
x: (transformedPos.x + 0.5) * globalConfig.tileSize, x: destX + globalConfig.halfTileSize,
y: (transformedPos.y + 0.5) * globalConfig.tileSize, y: destY + globalConfig.halfTileSize,
angle: Math.radians(angle), angle: Math.radians(angle),
size: globalConfig.tileSize, size: globalConfig.tileSize,
}); });
} }
} }
} }
}

View File

@ -0,0 +1,99 @@
import { globalConfig } from "../../core/config";
import { Loader } from "../../core/loader";
import { BaseItem } from "../base_item";
import { enumColors } from "../colors";
import { DisplayComponent } from "../components/display";
import { GameSystemWithFilter } from "../game_system_with_filter";
import { ColorItem, COLOR_ITEM_SINGLETONS } from "../items/color_item";
import { MapChunkView } from "../map_chunk_view";
import { BooleanItem } from "../items/boolean_item";
export class DisplaySystem extends GameSystemWithFilter {
constructor(root) {
super(root, [DisplayComponent]);
/** @type {Object<string, import("../../core/draw_utils").AtlasSprite>} */
this.displaySprites = {};
for (const colorId in enumColors) {
if (colorId === enumColors.uncolored) {
continue;
}
this.displaySprites[colorId] = Loader.getSprite("sprites/wires/display/" + colorId + ".png");
}
}
/**
* Returns the color / value a display should show
* @param {BaseItem} value
* @returns {BaseItem}
*/
getDisplayItem(value) {
if (!value) {
return null;
}
switch (value.getItemType()) {
case "boolean": {
return /** @type {BooleanItem} */ (value).value
? COLOR_ITEM_SINGLETONS[enumColors.white]
: null;
}
case "color": {
const item = /**@type {ColorItem} */ (value);
return item.color === enumColors.uncolored ? null : item;
}
case "shape": {
return value;
}
default:
assertAlways(false, "Unknown item type: " + value.getItemType());
}
}
/**
* Draws a given chunk
* @param {import("../../core/draw_utils").DrawParameters} parameters
* @param {MapChunkView} chunk
*/
drawChunk(parameters, chunk) {
const contents = chunk.containedEntitiesByLayer.regular;
for (let i = 0; i < contents.length; ++i) {
const entity = contents[i];
if (entity && entity.components.Display) {
const pinsComp = entity.components.WiredPins;
const network = pinsComp.slots[0].linkedNetwork;
if (!network || !network.currentValue) {
continue;
}
const value = this.getDisplayItem(network.currentValue);
if (!value) {
continue;
}
const origin = entity.components.StaticMapEntity.origin;
if (value.getItemType() === "color") {
this.displaySprites[/** @type {ColorItem} */ (value).color].drawCachedCentered(
parameters,
(origin.x + 0.5) * globalConfig.tileSize,
(origin.y + 0.5) * globalConfig.tileSize,
globalConfig.tileSize
);
} else if (value.getItemType() === "shape") {
value.drawItemCenteredClipped(
(origin.x + 0.5) * globalConfig.tileSize,
(origin.y + 0.5) * globalConfig.tileSize,
parameters,
30
);
}
}
}
}
}

View File

@ -5,6 +5,14 @@ import { T } from "../../translations";
import { HubComponent } from "../components/hub"; import { HubComponent } from "../components/hub";
import { Entity } from "../entity"; import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter"; import { GameSystemWithFilter } from "../game_system_with_filter";
import { globalConfig } from "../../core/config";
import { smoothenDpi } from "../../core/dpi_manager";
import { drawSpriteClipped } from "../../core/draw_utils";
import { Rectangle } from "../../core/rectangle";
import { ORIGINAL_SPRITE_SCALE } from "../../core/sprites";
const HUB_SIZE_TILES = 4;
const HUB_SIZE_PIXELS = HUB_SIZE_TILES * globalConfig.tileSize;
export class HubSystem extends GameSystemWithFilter { export class HubSystem extends GameSystemWithFilter {
constructor(root) { constructor(root) {
@ -13,8 +21,13 @@ export class HubSystem extends GameSystemWithFilter {
this.hubSprite = Loader.getSprite("sprites/buildings/hub.png"); this.hubSprite = Loader.getSprite("sprites/buildings/hub.png");
} }
/**
* @param {DrawParameters} parameters
*/
draw(parameters) { draw(parameters) {
this.forEachMatchingEntityOnScreen(parameters, this.drawEntity.bind(this)); for (let i = 0; i < this.allEntities.length; ++i) {
this.drawEntity(parameters, this.allEntities[i]);
}
} }
update() { update() {
@ -27,35 +40,42 @@ export class HubSystem extends GameSystemWithFilter {
); );
} }
} }
/** /**
* @param {DrawParameters} parameters *
* @param {Entity} entity * @param {HTMLCanvasElement} canvas
* @param {CanvasRenderingContext2D} context
* @param {number} w
* @param {number} h
* @param {number} dpi
*/ */
drawEntity(parameters, entity) { redrawHubBaseTexture(canvas, context, w, h, dpi) {
const context = parameters.context; // This method is quite ugly, please ignore it!
const staticComp = entity.components.StaticMapEntity;
if (!staticComp.shouldBeDrawn(parameters)) { context.scale(dpi, dpi);
return;
}
const pos = staticComp.getTileSpaceBounds().getCenter().toWorldSpace(); const parameters = new DrawParameters({
context,
visibleRect: new Rectangle(0, 0, w, h),
desiredAtlasScale: ORIGINAL_SPRITE_SCALE,
zoomLevel: dpi * 0.75,
root: this.root,
});
// Background context.clearRect(0, 0, w, h);
staticComp.drawSpriteOnFullEntityBounds(parameters, this.hubSprite, 2.2);
this.hubSprite.draw(context, 0, 0, w, h);
const definition = this.root.hubGoals.currentGoal.definition; const definition = this.root.hubGoals.currentGoal.definition;
definition.drawCentered(45, 58, parameters, 36);
definition.draw(pos.x - 25, pos.y - 10, parameters, 40);
const goals = this.root.hubGoals.currentGoal; const goals = this.root.hubGoals.currentGoal;
const textOffsetX = 2; const textOffsetX = 70;
const textOffsetY = -6; const textOffsetY = 61;
// Deliver count // Deliver count
const delivered = this.root.hubGoals.getCurrentGoalDelivered(); const delivered = this.root.hubGoals.getCurrentGoalDelivered();
const deliveredText = "" + formatBigNumber(delivered);
if (delivered > 9999) { if (delivered > 9999) {
context.font = "bold 16px GameFont"; context.font = "bold 16px GameFont";
@ -66,52 +86,87 @@ export class HubSystem extends GameSystemWithFilter {
} }
context.fillStyle = "#64666e"; context.fillStyle = "#64666e";
context.textAlign = "left"; context.textAlign = "left";
context.fillText("" + formatBigNumber(delivered), pos.x + textOffsetX, pos.y + textOffsetY); context.fillText(deliveredText, textOffsetX, textOffsetY);
// Required // Required
context.font = "13px GameFont"; context.font = "13px GameFont";
context.fillStyle = "#a4a6b0"; context.fillStyle = "#a4a6b0";
context.fillText( context.fillText("/ " + formatBigNumber(goals.required), textOffsetX, textOffsetY + 13);
"/ " + formatBigNumber(goals.required),
pos.x + textOffsetX,
pos.y + textOffsetY + 13
);
// Reward // Reward
const rewardText = T.storyRewards[goals.reward].title.toUpperCase(); const rewardText = T.storyRewards[goals.reward].title.toUpperCase();
if (rewardText.length > 12) { if (rewardText.length > 12) {
context.font = "bold 9px GameFont"; context.font = "bold 8px GameFont";
} else { } else {
context.font = "bold 11px GameFont"; context.font = "bold 10px GameFont";
} }
context.fillStyle = "#fd0752"; context.fillStyle = "#fd0752";
context.textAlign = "center"; context.textAlign = "center";
context.fillText(rewardText, pos.x, pos.y + 46); context.fillText(rewardText, HUB_SIZE_PIXELS / 2, 105);
// Level // Level "8"
context.font = "bold 11px GameFont"; context.font = "bold 10px GameFont";
context.fillStyle = "#fff"; context.fillStyle = "#fff";
context.fillText("" + this.root.hubGoals.level, pos.x - 42, pos.y - 36); context.fillText("" + this.root.hubGoals.level, 27, 32);
// Texts // "LVL"
context.textAlign = "center"; context.textAlign = "center";
context.fillStyle = "#fff"; context.fillStyle = "#fff";
context.font = "bold 7px GameFont"; context.font = "bold 6px GameFont";
context.fillText(T.buildings.hub.levelShortcut, pos.x - 42, pos.y - 47); context.fillText(T.buildings.hub.levelShortcut, 27, 22);
// "Deliver"
context.fillStyle = "#64666e"; context.fillStyle = "#64666e";
context.font = "bold 11px GameFont"; context.font = "bold 10px GameFont";
context.fillText(T.buildings.hub.deliver.toUpperCase(), pos.x, pos.y - 40); context.fillText(T.buildings.hub.deliver.toUpperCase(), HUB_SIZE_PIXELS / 2, 30);
// "To unlock"
const unlockText = T.buildings.hub.toUnlock.toUpperCase(); const unlockText = T.buildings.hub.toUnlock.toUpperCase();
if (unlockText.length > 15) { if (unlockText.length > 15) {
context.font = "bold 8px GameFont"; context.font = "bold 8px GameFont";
} else { } else {
context.font = "bold 11px GameFont"; context.font = "bold 10px GameFont";
} }
context.fillText(T.buildings.hub.toUnlock.toUpperCase(), pos.x, pos.y + 30); context.fillText(T.buildings.hub.toUnlock.toUpperCase(), HUB_SIZE_PIXELS / 2, 92);
context.textAlign = "left"; context.textAlign = "left";
} }
/**
* @param {DrawParameters} parameters
* @param {Entity} entity
*/
drawEntity(parameters, entity) {
const staticComp = entity.components.StaticMapEntity;
if (!staticComp.shouldBeDrawn(parameters)) {
return;
}
// Deliver count
const delivered = this.root.hubGoals.getCurrentGoalDelivered();
const deliveredText = "" + formatBigNumber(delivered);
const dpi = smoothenDpi(globalConfig.shapesSharpness * parameters.zoomLevel);
const canvas = parameters.root.buffers.getForKey({
key: "hub",
subKey: dpi + "/" + this.root.hubGoals.level + "/" + deliveredText,
w: globalConfig.tileSize * 4,
h: globalConfig.tileSize * 4,
dpi,
redrawMethod: this.redrawHubBaseTexture.bind(this),
});
const extrude = 8;
drawSpriteClipped({
parameters,
sprite: canvas,
x: staticComp.origin.x * globalConfig.tileSize - extrude,
y: staticComp.origin.y * globalConfig.tileSize - extrude,
w: HUB_SIZE_PIXELS + 2 * extrude,
h: HUB_SIZE_PIXELS + 2 * extrude,
originalW: HUB_SIZE_PIXELS * dpi,
originalH: HUB_SIZE_PIXELS * dpi,
});
}
} }

View File

@ -3,9 +3,8 @@ import { DrawParameters } from "../../core/draw_parameters";
import { fastArrayDelete } from "../../core/utils"; import { fastArrayDelete } from "../../core/utils";
import { enumDirectionToVector } from "../../core/vector"; import { enumDirectionToVector } from "../../core/vector";
import { ItemAcceptorComponent } from "../components/item_acceptor"; import { ItemAcceptorComponent } from "../components/item_acceptor";
import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter"; import { GameSystemWithFilter } from "../game_system_with_filter";
import { enumLayer } from "../root"; import { MapChunkView } from "../map_chunk_view";
export class ItemAcceptorSystem extends GameSystemWithFilter { export class ItemAcceptorSystem extends GameSystemWithFilter {
constructor(root) { constructor(root) {
@ -39,43 +38,45 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
} }
/** /**
* Draws the acceptor items
* @param {DrawParameters} parameters * @param {DrawParameters} parameters
* @param {MapChunkView} chunk
*/ */
draw(parameters) { drawChunk(parameters, chunk) {
this.forEachMatchingEntityOnScreen(parameters, this.drawEntityRegularLayer.bind(this)); const contents = chunk.containedEntitiesByLayer.regular;
} for (let i = 0; i < contents.length; ++i) {
const entity = contents[i];
/**
* @param {DrawParameters} parameters
* @param {Entity} entity
*/
drawEntityRegularLayer(parameters, entity) {
const staticComp = entity.components.StaticMapEntity;
const acceptorComp = entity.components.ItemAcceptor; const acceptorComp = entity.components.ItemAcceptor;
if (!acceptorComp) {
if (!staticComp.shouldBeDrawn(parameters)) { continue;
return;
} }
const staticComp = entity.components.StaticMapEntity;
for (let animIndex = 0; animIndex < acceptorComp.itemConsumptionAnimations.length; ++animIndex) { for (let animIndex = 0; animIndex < acceptorComp.itemConsumptionAnimations.length; ++animIndex) {
const { item, slotIndex, animProgress, direction } = acceptorComp.itemConsumptionAnimations[ const { item, slotIndex, animProgress, direction } = acceptorComp.itemConsumptionAnimations[
animIndex animIndex
]; ];
const slotData = acceptorComp.slots[slotIndex]; const slotData = acceptorComp.slots[slotIndex];
const realSlotPos = staticComp.localTileToWorld(slotData.pos);
if (!chunk.tileSpaceRectangle.containsPoint(realSlotPos.x, realSlotPos.y)) {
// Not within this chunk
continue;
}
const slotWorldPos = staticComp.applyRotationToVector(slotData.pos).add(staticComp.origin);
const fadeOutDirection = enumDirectionToVector[staticComp.localDirectionToWorld(direction)]; const fadeOutDirection = enumDirectionToVector[staticComp.localDirectionToWorld(direction)];
const finalTile = slotWorldPos.subScalars( const finalTile = realSlotPos.subScalars(
fadeOutDirection.x * (animProgress / 2 - 0.5), fadeOutDirection.x * (animProgress / 2 - 0.5),
fadeOutDirection.y * (animProgress / 2 - 0.5) fadeOutDirection.y * (animProgress / 2 - 0.5)
); );
item.draw(
item.drawItemCenteredClipped(
(finalTile.x + 0.5) * globalConfig.tileSize, (finalTile.x + 0.5) * globalConfig.tileSize,
(finalTile.y + 0.5) * globalConfig.tileSize, (finalTile.y + 0.5) * globalConfig.tileSize,
parameters parameters,
globalConfig.defaultItemDiameter
); );
} }
} }
} }
}

Some files were not shown because too many files have changed in this diff Show More