Merge pull request #1 from jimmykarns/master
update removed enum layer with tests and bridge reserves
BIN
res/ui/building_icons/virtual_processor.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 238 KiB After Width: | Height: | Size: 254 KiB |
Before Width: | Height: | Size: 567 KiB After Width: | Height: | Size: 601 KiB |
@ -277,6 +277,7 @@
|
|||||||
<key type="filename">sprites/blueprints/underground_belt_entry.png</key>
|
<key type="filename">sprites/blueprints/underground_belt_entry.png</key>
|
||||||
<key type="filename">sprites/blueprints/underground_belt_exit-tier2.png</key>
|
<key type="filename">sprites/blueprints/underground_belt_exit-tier2.png</key>
|
||||||
<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-coating.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/display.png</key>
|
||||||
@ -295,6 +296,7 @@
|
|||||||
<key type="filename">sprites/buildings/underground_belt_entry.png</key>
|
<key type="filename">sprites/buildings/underground_belt_entry.png</key>
|
||||||
<key type="filename">sprites/buildings/underground_belt_exit-tier2.png</key>
|
<key type="filename">sprites/buildings/underground_belt_exit-tier2.png</key>
|
||||||
<key type="filename">sprites/buildings/underground_belt_exit.png</key>
|
<key type="filename">sprites/buildings/underground_belt_exit.png</key>
|
||||||
|
<key type="filename">sprites/buildings/wire_tunnel-coating.png</key>
|
||||||
<key type="filename">sprites/buildings/wire_tunnel.png</key>
|
<key type="filename">sprites/buildings/wire_tunnel.png</key>
|
||||||
<key type="filename">sprites/wires/lever_on.png</key>
|
<key type="filename">sprites/wires/lever_on.png</key>
|
||||||
<key type="filename">sprites/wires/sets/color_cross.png</key>
|
<key type="filename">sprites/wires/sets/color_cross.png</key>
|
||||||
@ -533,6 +535,8 @@
|
|||||||
</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/network_conflict.png</key>
|
||||||
|
<key type="filename">sprites/wires/network_empty.png</key>
|
||||||
<key type="filename">sprites/wires/wires_preview.png</key>
|
<key type="filename">sprites/wires/wires_preview.png</key>
|
||||||
<struct type="IndividualSpriteSettings">
|
<struct type="IndividualSpriteSettings">
|
||||||
<key>pivotPoint</key>
|
<key>pivotPoint</key>
|
||||||
|
BIN
res_raw/sprites/blueprints/virtual_processor-analyzer.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
res_raw/sprites/blueprints/virtual_processor-rotater.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
res_raw/sprites/blueprints/virtual_processor-shapecompare.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
res_raw/sprites/blueprints/virtual_processor-unstacker.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
res_raw/sprites/blueprints/virtual_processor.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
res_raw/sprites/buildings/virtual_processor-analyzer.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
res_raw/sprites/buildings/virtual_processor-rotater.png
Normal file
After Width: | Height: | Size: 9.3 KiB |
BIN
res_raw/sprites/buildings/virtual_processor-shapecompare.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
res_raw/sprites/buildings/virtual_processor-unstacker.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
res_raw/sprites/buildings/virtual_processor.png
Normal file
After Width: | Height: | Size: 10 KiB |
@ -1,38 +1,38 @@
|
|||||||
$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, display;
|
constant_signal, logic_gate, lever, filter, wire_tunnel, display, virtual_processor;
|
||||||
|
|
||||||
@each $building in $buildings {
|
@each $building in $buildings {
|
||||||
[data-icon="building_icons/#{$building}.png"] {
|
[data-icon="building_icons/#{$building}.png"] {
|
||||||
background-image: uiResource("res/ui/building_icons/#{$building}.png") !important;
|
background-image: uiResource("res/ui/building_icons/#{$building}.png") !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$buildingsAndVariants: belt, splitter, splitter-compact, splitter-compact-inverse, underground_belt,
|
$buildingsAndVariants: belt, splitter, splitter-compact, splitter-compact-inverse, underground_belt,
|
||||||
underground_belt-tier2, miner, miner-chainable, cutter, cutter-quad, rotater, rotater-ccw, rotater-fl,
|
underground_belt-tier2, miner, miner-chainable, cutter, cutter-quad, rotater, rotater-ccw, rotater-fl,
|
||||||
stacker, mixer, painter, painter-double, painter-quad, trash, trash-storage;
|
stacker, mixer, painter, painter-double, painter-quad, trash, trash-storage;
|
||||||
@each $building in $buildingsAndVariants {
|
@each $building in $buildingsAndVariants {
|
||||||
[data-icon="building_tutorials/#{$building}.png"] {
|
[data-icon="building_tutorials/#{$building}.png"] {
|
||||||
background-image: uiResource("res/ui/building_tutorials/#{$building}.png") !important;
|
background-image: uiResource("res/ui/building_tutorials/#{$building}.png") !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case
|
// Special case
|
||||||
[data-icon="building_tutorials/painter-mirrored.png"] {
|
[data-icon="building_tutorials/painter-mirrored.png"] {
|
||||||
background-image: uiResource("res/ui/building_tutorials/painter.png") !important;
|
background-image: uiResource("res/ui/building_tutorials/painter.png") !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
$icons: notification_saved, notification_success, notification_upgrade;
|
$icons: notification_saved, notification_success, notification_upgrade;
|
||||||
@each $icon in $icons {
|
@each $icon in $icons {
|
||||||
[data-icon="icons/#{$icon}.png"] {
|
[data-icon="icons/#{$icon}.png"] {
|
||||||
background-image: uiResource("res/ui/icons/#{$icon}.png") !important;
|
background-image: uiResource("res/ui/icons/#{$icon}.png") !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$languages: en, de, cs, da, et, es-419, fr, it, pt-BR, sv, tr, el, ru, uk, zh-TW, zh-CN, nb, mt-MT, ar, nl, vi,
|
$languages: en, de, cs, da, et, es-419, fr, it, pt-BR, sv, tr, el, ru, uk, zh-TW, zh-CN, nb, mt-MT, ar, nl, vi,
|
||||||
th, hu, pl, ja, kor, no, pt-PT;
|
th, hu, pl, ja, kor, no, pt-PT;
|
||||||
|
|
||||||
@each $language in $languages {
|
@each $language in $languages {
|
||||||
[data-languageicon="#{$language}"] {
|
[data-languageicon="#{$language}"] {
|
||||||
background-image: uiResource("languages/#{$language}.svg") !important;
|
background-image: uiResource("languages/#{$language}.svg") !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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,
|
||||||
|
@ -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 */
|
||||||
};
|
};
|
||||||
|
@ -53,27 +53,6 @@ export class Rectangle {
|
|||||||
return a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom;
|
return a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a rectangle arround a rotated point
|
|
||||||
* @param {Array<Vector>} points
|
|
||||||
* @param {number} angle
|
|
||||||
* @returns {Rectangle}
|
|
||||||
*/
|
|
||||||
static getAroundPointsRotated(points, angle) {
|
|
||||||
let minX = 1e10;
|
|
||||||
let minY = 1e10;
|
|
||||||
let maxX = -1e10;
|
|
||||||
let maxY = -1e10;
|
|
||||||
for (let i = 0; i < points.length; ++i) {
|
|
||||||
const rotated = points[i].rotated(angle);
|
|
||||||
minX = Math.min(minX, rotated.x);
|
|
||||||
minY = Math.min(minY, rotated.y);
|
|
||||||
maxX = Math.max(maxX, rotated.x);
|
|
||||||
maxY = Math.max(maxY, rotated.y);
|
|
||||||
}
|
|
||||||
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies this instance
|
* Copies this instance
|
||||||
* @returns {Rectangle}
|
* @returns {Rectangle}
|
||||||
@ -82,28 +61,6 @@ export class Rectangle {
|
|||||||
return new Rectangle(this.x, this.y, this.w, this.h);
|
return new Rectangle(this.x, this.y, this.w, this.h);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures the rectangle contains the given square
|
|
||||||
* @param {number} centerX
|
|
||||||
* @param {number} centerY
|
|
||||||
* @param {number} halfWidth
|
|
||||||
* @param {number} halfHeight
|
|
||||||
*/
|
|
||||||
extendBySquare(centerX, centerY, halfWidth, halfHeight) {
|
|
||||||
if (this.isEmpty()) {
|
|
||||||
// Just assign values since this rectangle is empty
|
|
||||||
this.x = centerX - halfWidth;
|
|
||||||
this.y = centerY - halfHeight;
|
|
||||||
this.w = halfWidth * 2;
|
|
||||||
this.h = halfHeight * 2;
|
|
||||||
} else {
|
|
||||||
this.setLeft(Math.min(this.x, centerX - halfWidth));
|
|
||||||
this.setRight(Math.max(this.right(), centerX + halfWidth));
|
|
||||||
this.setTop(Math.min(this.y, centerY - halfHeight));
|
|
||||||
this.setBottom(Math.max(this.bottom(), centerY + halfHeight));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if this rectangle is empty
|
* Returns if this rectangle is empty
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
@ -259,14 +216,6 @@ export class Rectangle {
|
|||||||
return new Rectangle(this.x - amount, this.y - amount, this.w + 2 * amount, this.h + 2 * amount);
|
return new Rectangle(this.x - amount, this.y - amount, this.w + 2 * amount, this.h + 2 * amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper for computing a culling area. Returns the top left tile
|
|
||||||
* @returns {Vector}
|
|
||||||
*/
|
|
||||||
getMinStartTile() {
|
|
||||||
return new Vector(this.x, this.y).snapWorldToTile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if the given rectangle is contained
|
* Returns if the given rectangle is contained
|
||||||
* @param {Rectangle} rect
|
* @param {Rectangle} rect
|
||||||
@ -394,7 +343,7 @@ export class Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new recangle in tile space which includes all tiles which are visible in this rect
|
* Returns a new rectangle in tile space which includes all tiles which are visible in this rect
|
||||||
* @returns {Rectangle}
|
* @returns {Rectangle}
|
||||||
*/
|
*/
|
||||||
toTileCullRectangle() {
|
toTileCullRectangle() {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
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";
|
||||||
|
|
||||||
@ -57,7 +58,22 @@ export class BaseItem extends BasicSerializableObject {
|
|||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
* @param {number=} diameter
|
* @param {number=} diameter
|
||||||
*/
|
*/
|
||||||
drawCentered(x, y, parameters, diameter) {}
|
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;
|
||||||
|
@ -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].drawCentered(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];
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
151
src/js/game/buildings/virtual_processor.js
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import { Vector, enumDirection } from "../../core/vector";
|
||||||
|
import { LogicGateComponent, enumLogicGateType } from "../components/logic_gate";
|
||||||
|
import { WiredPinsComponent, enumPinSlotType } from "../components/wired_pins";
|
||||||
|
import { Entity } from "../entity";
|
||||||
|
import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
|
||||||
|
import { GameRoot } from "../root";
|
||||||
|
|
||||||
|
/** @enum {string} */
|
||||||
|
export const enumVirtualProcessorVariants = {
|
||||||
|
analyzer: "analyzer",
|
||||||
|
rotater: "rotater",
|
||||||
|
unstacker: "unstacker",
|
||||||
|
shapecompare: "shapecompare",
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @enum {string} */
|
||||||
|
export const enumVariantToGate = {
|
||||||
|
[defaultBuildingVariant]: enumLogicGateType.cutter,
|
||||||
|
[enumVirtualProcessorVariants.analyzer]: enumLogicGateType.analyzer,
|
||||||
|
[enumVirtualProcessorVariants.rotater]: enumLogicGateType.rotater,
|
||||||
|
[enumVirtualProcessorVariants.unstacker]: enumLogicGateType.unstacker,
|
||||||
|
[enumVirtualProcessorVariants.shapecompare]: enumLogicGateType.shapecompare,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class MetaVirtualProcessorBuilding extends MetaBuilding {
|
||||||
|
constructor() {
|
||||||
|
super("virtual_processor");
|
||||||
|
}
|
||||||
|
|
||||||
|
getSilhouetteColor() {
|
||||||
|
return "#823cab";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GameRoot} root
|
||||||
|
*/
|
||||||
|
getIsUnlocked(root) {
|
||||||
|
// @todo
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {"wires"} **/
|
||||||
|
getLayer() {
|
||||||
|
return "wires";
|
||||||
|
}
|
||||||
|
|
||||||
|
getDimensions() {
|
||||||
|
return new Vector(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAvailableVariants() {
|
||||||
|
return [
|
||||||
|
defaultBuildingVariant,
|
||||||
|
enumVirtualProcessorVariants.rotater,
|
||||||
|
enumVirtualProcessorVariants.unstacker,
|
||||||
|
enumVirtualProcessorVariants.analyzer,
|
||||||
|
enumVirtualProcessorVariants.shapecompare,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getRenderPins() {
|
||||||
|
// We already have it included
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Entity} entity
|
||||||
|
* @param {number} rotationVariant
|
||||||
|
*/
|
||||||
|
updateVariants(entity, rotationVariant, variant) {
|
||||||
|
const gateType = enumVariantToGate[variant];
|
||||||
|
entity.components.LogicGate.type = gateType;
|
||||||
|
const pinComp = entity.components.WiredPins;
|
||||||
|
switch (gateType) {
|
||||||
|
case enumLogicGateType.cutter:
|
||||||
|
case enumLogicGateType.analyzer:
|
||||||
|
case enumLogicGateType.unstacker: {
|
||||||
|
pinComp.setSlots([
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.left,
|
||||||
|
type: enumPinSlotType.logicalEjector,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.right,
|
||||||
|
type: enumPinSlotType.logicalEjector,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.bottom,
|
||||||
|
type: enumPinSlotType.logicalAcceptor,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case enumLogicGateType.rotater: {
|
||||||
|
pinComp.setSlots([
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.top,
|
||||||
|
type: enumPinSlotType.logicalEjector,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.bottom,
|
||||||
|
type: enumPinSlotType.logicalAcceptor,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case enumLogicGateType.shapecompare: {
|
||||||
|
pinComp.setSlots([
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.top,
|
||||||
|
type: enumPinSlotType.logicalEjector,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.left,
|
||||||
|
type: enumPinSlotType.logicalAcceptor,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.right,
|
||||||
|
type: enumPinSlotType.logicalAcceptor,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assertAlways("unknown logic gate type: " + gateType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the entity at the given location
|
||||||
|
* @param {Entity} entity
|
||||||
|
*/
|
||||||
|
setupEntityComponents(entity) {
|
||||||
|
entity.addComponent(
|
||||||
|
new WiredPinsComponent({
|
||||||
|
slots: [],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
entity.addComponent(new LogicGateComponent({}));
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +1,36 @@
|
|||||||
import { Component } from "../component";
|
import { Component } from "../component";
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
export const enumLogicGateType = {
|
export const enumLogicGateType = {
|
||||||
and: "and",
|
and: "and",
|
||||||
not: "not",
|
not: "not",
|
||||||
xor: "xor",
|
xor: "xor",
|
||||||
or: "or",
|
or: "or",
|
||||||
transistor: "transistor",
|
transistor: "transistor",
|
||||||
};
|
|
||||||
|
analyzer: "analyzer",
|
||||||
export class LogicGateComponent extends Component {
|
rotater: "rotater",
|
||||||
static getId() {
|
unstacker: "unstacker",
|
||||||
return "LogicGate";
|
cutter: "cutter",
|
||||||
}
|
shapecompare: "shapecompare",
|
||||||
|
};
|
||||||
duplicateWithoutContents() {
|
|
||||||
return new LogicGateComponent({ type: this.type });
|
export class LogicGateComponent extends Component {
|
||||||
}
|
static getId() {
|
||||||
|
return "LogicGate";
|
||||||
/**
|
}
|
||||||
*
|
|
||||||
* @param {object} param0
|
duplicateWithoutContents() {
|
||||||
* @param {enumLogicGateType=} param0.type
|
return new LogicGateComponent({ type: this.type });
|
||||||
*/
|
}
|
||||||
constructor({ type = enumLogicGateType.and }) {
|
|
||||||
super();
|
/**
|
||||||
this.type = type;
|
*
|
||||||
}
|
* @param {object} param0
|
||||||
}
|
* @param {enumLogicGateType=} param0.type
|
||||||
|
*/
|
||||||
|
constructor({ type = enumLogicGateType.and }) {
|
||||||
|
super();
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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";
|
||||||
@ -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;
|
||||||
|
|
||||||
@ -390,33 +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);
|
||||||
|
|
||||||
// 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 === "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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,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 +
|
||||||
@ -472,8 +462,22 @@ 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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -374,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
|
||||||
|
@ -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() {
|
||||||
|
@ -64,11 +64,16 @@ export class HUDWireInfo extends BaseHUDPart {
|
|||||||
const network = networks[0];
|
const network = networks[0];
|
||||||
|
|
||||||
if (network.valueConflict) {
|
if (network.valueConflict) {
|
||||||
this.spriteConflict.draw(parameters.context, mousePos.x + 10, mousePos.y - 10, 40, 40);
|
this.spriteConflict.draw(parameters.context, mousePos.x + 15, mousePos.y - 10, 60, 60);
|
||||||
} else if (!network.currentValue) {
|
} else if (!network.currentValue) {
|
||||||
this.spriteEmpty.draw(parameters.context, mousePos.x + 10, mousePos.y - 10, 40, 40);
|
this.spriteEmpty.draw(parameters.context, mousePos.x + 15, mousePos.y - 10, 60, 60);
|
||||||
} else {
|
} else {
|
||||||
network.currentValue.drawCentered(mousePos.x + 20, mousePos.y, parameters, 40);
|
network.currentValue.drawItemCenteredClipped(
|
||||||
|
mousePos.x + 40,
|
||||||
|
mousePos.y + 10,
|
||||||
|
parameters,
|
||||||
|
60
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,27 @@
|
|||||||
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";
|
||||||
import { MetaLogicGateBuilding } from "../../buildings/logic_gate";
|
import { MetaLogicGateBuilding } from "../../buildings/logic_gate";
|
||||||
import { MetaLeverBuilding } from "../../buildings/lever";
|
import { MetaLeverBuilding } from "../../buildings/lever";
|
||||||
import { MetaWireTunnelBuilding } from "../../buildings/wire_tunnel";
|
import { MetaWireTunnelBuilding } from "../../buildings/wire_tunnel";
|
||||||
|
import { MetaVirtualProcessorBuilding } from "../../buildings/virtual_processor";
|
||||||
const supportedBuildings = [
|
|
||||||
MetaWireBuilding,
|
const supportedBuildings = [
|
||||||
MetaWireTunnelBuilding,
|
MetaWireBuilding,
|
||||||
MetaConstantSignalBuilding,
|
MetaWireTunnelBuilding,
|
||||||
MetaLogicGateBuilding,
|
MetaConstantSignalBuilding,
|
||||||
MetaLeverBuilding,
|
MetaLogicGateBuilding,
|
||||||
];
|
MetaLeverBuilding,
|
||||||
|
MetaVirtualProcessorBuilding,
|
||||||
export class HUDWiresToolbar extends HUDBaseToolbar {
|
];
|
||||||
constructor(root) {
|
|
||||||
super(root, {
|
export class HUDWiresToolbar extends HUDBaseToolbar {
|
||||||
supportedBuildings,
|
constructor(root) {
|
||||||
visibilityCondition: () =>
|
super(root, {
|
||||||
!this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "wires",
|
supportedBuildings,
|
||||||
htmlElementId: "ingame_HUD_wires_toolbar",
|
visibilityCondition: () =>
|
||||||
});
|
!this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "wires",
|
||||||
}
|
htmlElementId: "ingame_HUD_wires_toolbar",
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ 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 } 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() {
|
||||||
@ -46,7 +47,7 @@ export class BooleanItem extends BaseItem {
|
|||||||
* @param {number} diameter
|
* @param {number} diameter
|
||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
*/
|
*/
|
||||||
drawCentered(x, y, parameters, diameter = 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");
|
||||||
|
@ -5,6 +5,7 @@ import { types } from "../../savegame/serialization";
|
|||||||
import { BaseItem } 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() {
|
||||||
@ -54,23 +55,33 @@ export class ColorItem extends BaseItem {
|
|||||||
* @param {number} diameter
|
* @param {number} diameter
|
||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
*/
|
*/
|
||||||
drawCentered(x, y, parameters, diameter = 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 = diameter + "/" + dpi;
|
|
||||||
const canvas = parameters.root.buffers.getForKey({
|
const canvas = parameters.root.buffers.getForKey({
|
||||||
key,
|
key: "coloritem",
|
||||||
subKey: this.color,
|
subKey: key,
|
||||||
w: diameter,
|
w: realDiameter,
|
||||||
h: diameter,
|
h: realDiameter,
|
||||||
dpi,
|
dpi,
|
||||||
redrawMethod: this.bufferGenerator,
|
redrawMethod: this.bufferGenerator,
|
||||||
});
|
});
|
||||||
parameters.context.drawImage(canvas, x - diameter / 2, y - diameter / 2, diameter, diameter);
|
|
||||||
|
drawSpriteClipped({
|
||||||
|
parameters,
|
||||||
|
sprite: canvas,
|
||||||
|
x: x - realDiameter / 2,
|
||||||
|
y: y - realDiameter / 2,
|
||||||
|
w: realDiameter,
|
||||||
|
h: realDiameter,
|
||||||
|
originalW: realDiameter * dpi,
|
||||||
|
originalH: realDiameter * dpi,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -3,6 +3,7 @@ import { types } from "../../savegame/serialization";
|
|||||||
import { BaseItem } 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() {
|
||||||
@ -55,7 +56,7 @@ export class ShapeItem extends BaseItem {
|
|||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
* @param {number=} diameter
|
* @param {number=} diameter
|
||||||
*/
|
*/
|
||||||
drawCentered(x, y, parameters, diameter) {
|
drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
|
||||||
this.definition.drawCentered(x, y, parameters, diameter);
|
this.definition.drawCentered(x, y, parameters, diameter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,456 +1,457 @@
|
|||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { InputReceiver } from "../core/input_receiver";
|
import { InputReceiver } from "../core/input_receiver";
|
||||||
import { Application } from "../application";
|
import { Application } from "../application";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
import { Signal, STOP_PROPAGATION } from "../core/signal";
|
import { Signal, STOP_PROPAGATION } from "../core/signal";
|
||||||
import { IS_MOBILE } from "../core/config";
|
import { IS_MOBILE } from "../core/config";
|
||||||
import { T } from "../translations";
|
import { T } from "../translations";
|
||||||
function key(str) {
|
function key(str) {
|
||||||
return str.toUpperCase().charCodeAt(0);
|
return str.toUpperCase().charCodeAt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const KEYMAPPINGS = {
|
export const KEYMAPPINGS = {
|
||||||
general: {
|
general: {
|
||||||
confirm: { keyCode: 13 }, // enter
|
confirm: { keyCode: 13 }, // enter
|
||||||
back: { keyCode: 27, builtin: true }, // escape
|
back: { keyCode: 27, builtin: true }, // escape
|
||||||
},
|
},
|
||||||
|
|
||||||
ingame: {
|
ingame: {
|
||||||
menuOpenShop: { keyCode: key("F") },
|
menuOpenShop: { keyCode: key("F") },
|
||||||
menuOpenStats: { keyCode: key("G") },
|
menuOpenStats: { keyCode: key("G") },
|
||||||
menuClose: { keyCode: key("Q") },
|
menuClose: { keyCode: key("Q") },
|
||||||
|
|
||||||
toggleHud: { keyCode: 113 }, // F2
|
toggleHud: { keyCode: 113 }, // F2
|
||||||
exportScreenshot: { keyCode: 114 }, // F3PS
|
exportScreenshot: { keyCode: 114 }, // F3PS
|
||||||
toggleFPSInfo: { keyCode: 115 }, // F4
|
toggleFPSInfo: { keyCode: 115 }, // F4
|
||||||
|
|
||||||
switchLayers: { keyCode: key("Y") },
|
switchLayers: { keyCode: key("Y") },
|
||||||
},
|
},
|
||||||
|
|
||||||
navigation: {
|
navigation: {
|
||||||
mapMoveUp: { keyCode: key("W") },
|
mapMoveUp: { keyCode: key("W") },
|
||||||
mapMoveRight: { keyCode: key("D") },
|
mapMoveRight: { keyCode: key("D") },
|
||||||
mapMoveDown: { keyCode: key("S") },
|
mapMoveDown: { keyCode: key("S") },
|
||||||
mapMoveLeft: { keyCode: key("A") },
|
mapMoveLeft: { keyCode: key("A") },
|
||||||
mapMoveFaster: { keyCode: 16 }, //shift
|
mapMoveFaster: { keyCode: 16 }, //shift
|
||||||
|
|
||||||
centerMap: { keyCode: 32 }, // SPACE
|
centerMap: { keyCode: 32 }, // SPACE
|
||||||
mapZoomIn: { keyCode: 187, repeated: true }, // "+"
|
mapZoomIn: { keyCode: 187, repeated: true }, // "+"
|
||||||
mapZoomOut: { keyCode: 189, repeated: true }, // "-"
|
mapZoomOut: { keyCode: 189, repeated: true }, // "-"
|
||||||
|
|
||||||
createMarker: { keyCode: key("M") },
|
createMarker: { keyCode: key("M") },
|
||||||
},
|
},
|
||||||
|
|
||||||
buildings: {
|
buildings: {
|
||||||
belt: { keyCode: key("1") },
|
belt: { keyCode: key("1") },
|
||||||
splitter: { keyCode: key("2") },
|
splitter: { keyCode: key("2") },
|
||||||
underground_belt: { keyCode: key("3") },
|
underground_belt: { keyCode: key("3") },
|
||||||
miner: { keyCode: key("4") },
|
miner: { keyCode: key("4") },
|
||||||
cutter: { keyCode: key("5") },
|
cutter: { keyCode: key("5") },
|
||||||
rotater: { keyCode: key("6") },
|
rotater: { keyCode: key("6") },
|
||||||
stacker: { keyCode: key("7") },
|
stacker: { keyCode: key("7") },
|
||||||
mixer: { keyCode: key("8") },
|
mixer: { keyCode: key("8") },
|
||||||
painter: { keyCode: key("9") },
|
painter: { keyCode: key("9") },
|
||||||
trash: { keyCode: key("0") },
|
trash: { keyCode: key("0") },
|
||||||
|
|
||||||
lever: { keyCode: key("L") },
|
lever: { keyCode: key("L") },
|
||||||
filter: { keyCode: key("B") },
|
filter: { keyCode: key("B") },
|
||||||
display: { keyCode: key("N") },
|
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") },
|
||||||
},
|
virtual_processor: { keyCode: key("5") },
|
||||||
|
},
|
||||||
placement: {
|
|
||||||
pipette: { keyCode: key("Q") },
|
placement: {
|
||||||
rotateWhilePlacing: { keyCode: key("R") },
|
pipette: { keyCode: key("Q") },
|
||||||
rotateInverseModifier: { keyCode: 16 }, // SHIFT
|
rotateWhilePlacing: { keyCode: key("R") },
|
||||||
cycleBuildingVariants: { keyCode: key("T") },
|
rotateInverseModifier: { keyCode: 16 }, // SHIFT
|
||||||
cycleBuildings: { keyCode: 9 }, // TAB
|
cycleBuildingVariants: { keyCode: key("T") },
|
||||||
switchDirectionLockSide: { keyCode: key("R") },
|
cycleBuildings: { keyCode: 9 }, // TAB
|
||||||
},
|
switchDirectionLockSide: { keyCode: key("R") },
|
||||||
|
},
|
||||||
massSelect: {
|
|
||||||
massSelectStart: { keyCode: 17 }, // CTRL
|
massSelect: {
|
||||||
massSelectSelectMultiple: { keyCode: 16 }, // SHIFT
|
massSelectStart: { keyCode: 17 }, // CTRL
|
||||||
massSelectCopy: { keyCode: key("C") },
|
massSelectSelectMultiple: { keyCode: 16 }, // SHIFT
|
||||||
massSelectCut: { keyCode: key("X") },
|
massSelectCopy: { keyCode: key("C") },
|
||||||
confirmMassDelete: { keyCode: 46 }, // DEL
|
massSelectCut: { keyCode: key("X") },
|
||||||
pasteLastBlueprint: { keyCode: key("V") },
|
confirmMassDelete: { keyCode: 46 }, // DEL
|
||||||
},
|
pasteLastBlueprint: { keyCode: key("V") },
|
||||||
|
},
|
||||||
placementModifiers: {
|
|
||||||
lockBeltDirection: { keyCode: 16 }, // SHIFT
|
placementModifiers: {
|
||||||
placementDisableAutoOrientation: { keyCode: 17 }, // CTRL
|
lockBeltDirection: { keyCode: 16 }, // SHIFT
|
||||||
placeMultiple: { keyCode: 16 }, // SHIFT
|
placementDisableAutoOrientation: { keyCode: 17 }, // CTRL
|
||||||
placeInverse: { keyCode: 18 }, // ALT
|
placeMultiple: { keyCode: 16 }, // SHIFT
|
||||||
},
|
placeInverse: { keyCode: 18 }, // ALT
|
||||||
};
|
},
|
||||||
|
};
|
||||||
// Assign ids
|
|
||||||
for (const categoryId in KEYMAPPINGS) {
|
// Assign ids
|
||||||
for (const mappingId in KEYMAPPINGS[categoryId]) {
|
for (const categoryId in KEYMAPPINGS) {
|
||||||
KEYMAPPINGS[categoryId][mappingId].id = mappingId;
|
for (const mappingId in KEYMAPPINGS[categoryId]) {
|
||||||
}
|
KEYMAPPINGS[categoryId][mappingId].id = mappingId;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
export const KEYCODE_LMB = 1;
|
|
||||||
export const KEYCODE_MMB = 2;
|
export const KEYCODE_LMB = 1;
|
||||||
export const KEYCODE_RMB = 3;
|
export const KEYCODE_MMB = 2;
|
||||||
|
export const KEYCODE_RMB = 3;
|
||||||
/**
|
|
||||||
* Returns a keycode -> string
|
/**
|
||||||
* @param {number} code
|
* Returns a keycode -> string
|
||||||
* @returns {string}
|
* @param {number} code
|
||||||
*/
|
* @returns {string}
|
||||||
export function getStringForKeyCode(code) {
|
*/
|
||||||
switch (code) {
|
export function getStringForKeyCode(code) {
|
||||||
case KEYCODE_LMB:
|
switch (code) {
|
||||||
return "LMB";
|
case KEYCODE_LMB:
|
||||||
case KEYCODE_MMB:
|
return "LMB";
|
||||||
return "MMB";
|
case KEYCODE_MMB:
|
||||||
case KEYCODE_RMB:
|
return "MMB";
|
||||||
return "RMB";
|
case KEYCODE_RMB:
|
||||||
case 4:
|
return "RMB";
|
||||||
return "MB4";
|
case 4:
|
||||||
case 5:
|
return "MB4";
|
||||||
return "MB5";
|
case 5:
|
||||||
case 8:
|
return "MB5";
|
||||||
return "⌫";
|
case 8:
|
||||||
case 9:
|
return "⌫";
|
||||||
return T.global.keys.tab;
|
case 9:
|
||||||
case 13:
|
return T.global.keys.tab;
|
||||||
return "⏎";
|
case 13:
|
||||||
case 16:
|
return "⏎";
|
||||||
return "⇪";
|
case 16:
|
||||||
case 17:
|
return "⇪";
|
||||||
return T.global.keys.control;
|
case 17:
|
||||||
case 18:
|
return T.global.keys.control;
|
||||||
return T.global.keys.alt;
|
case 18:
|
||||||
case 19:
|
return T.global.keys.alt;
|
||||||
return "PAUSE";
|
case 19:
|
||||||
case 20:
|
return "PAUSE";
|
||||||
return "CAPS";
|
case 20:
|
||||||
case 27:
|
return "CAPS";
|
||||||
return T.global.keys.escape;
|
case 27:
|
||||||
case 32:
|
return T.global.keys.escape;
|
||||||
return T.global.keys.space;
|
case 32:
|
||||||
case 33:
|
return T.global.keys.space;
|
||||||
return "PGUP";
|
case 33:
|
||||||
case 34:
|
return "PGUP";
|
||||||
return "PGDOWN";
|
case 34:
|
||||||
case 35:
|
return "PGDOWN";
|
||||||
return "END";
|
case 35:
|
||||||
case 36:
|
return "END";
|
||||||
return "HOME";
|
case 36:
|
||||||
case 37:
|
return "HOME";
|
||||||
return "⬅";
|
case 37:
|
||||||
case 38:
|
return "⬅";
|
||||||
return "⬆";
|
case 38:
|
||||||
case 39:
|
return "⬆";
|
||||||
return "➡";
|
case 39:
|
||||||
case 40:
|
return "➡";
|
||||||
return "⬇";
|
case 40:
|
||||||
case 44:
|
return "⬇";
|
||||||
return "PRNT";
|
case 44:
|
||||||
case 45:
|
return "PRNT";
|
||||||
return "INS";
|
case 45:
|
||||||
case 46:
|
return "INS";
|
||||||
return "DEL";
|
case 46:
|
||||||
case 93:
|
return "DEL";
|
||||||
return "SEL";
|
case 93:
|
||||||
case 96:
|
return "SEL";
|
||||||
return "NUM 0";
|
case 96:
|
||||||
case 97:
|
return "NUM 0";
|
||||||
return "NUM 1";
|
case 97:
|
||||||
case 98:
|
return "NUM 1";
|
||||||
return "NUM 2";
|
case 98:
|
||||||
case 99:
|
return "NUM 2";
|
||||||
return "NUM 3";
|
case 99:
|
||||||
case 100:
|
return "NUM 3";
|
||||||
return "NUM 4";
|
case 100:
|
||||||
case 101:
|
return "NUM 4";
|
||||||
return "NUM 5";
|
case 101:
|
||||||
case 102:
|
return "NUM 5";
|
||||||
return "NUM 6";
|
case 102:
|
||||||
case 103:
|
return "NUM 6";
|
||||||
return "NUM 7";
|
case 103:
|
||||||
case 104:
|
return "NUM 7";
|
||||||
return "NUM 8";
|
case 104:
|
||||||
case 105:
|
return "NUM 8";
|
||||||
return "NUM 9";
|
case 105:
|
||||||
case 106:
|
return "NUM 9";
|
||||||
return "*";
|
case 106:
|
||||||
case 107:
|
return "*";
|
||||||
return "+";
|
case 107:
|
||||||
case 109:
|
return "+";
|
||||||
return "-";
|
case 109:
|
||||||
case 110:
|
return "-";
|
||||||
return ".";
|
case 110:
|
||||||
case 111:
|
return ".";
|
||||||
return "/";
|
case 111:
|
||||||
case 112:
|
return "/";
|
||||||
return "F1";
|
case 112:
|
||||||
case 113:
|
return "F1";
|
||||||
return "F2";
|
case 113:
|
||||||
case 114:
|
return "F2";
|
||||||
return "F3";
|
case 114:
|
||||||
case 115:
|
return "F3";
|
||||||
return "F4";
|
case 115:
|
||||||
case 116:
|
return "F4";
|
||||||
return "F4";
|
case 116:
|
||||||
case 117:
|
return "F4";
|
||||||
return "F5";
|
case 117:
|
||||||
case 118:
|
return "F5";
|
||||||
return "F6";
|
case 118:
|
||||||
case 119:
|
return "F6";
|
||||||
return "F7";
|
case 119:
|
||||||
case 120:
|
return "F7";
|
||||||
return "F8";
|
case 120:
|
||||||
case 121:
|
return "F8";
|
||||||
return "F9";
|
case 121:
|
||||||
case 122:
|
return "F9";
|
||||||
return "F10";
|
case 122:
|
||||||
case 123:
|
return "F10";
|
||||||
return "F11";
|
case 123:
|
||||||
case 124:
|
return "F11";
|
||||||
return "F12";
|
case 124:
|
||||||
|
return "F12";
|
||||||
case 144:
|
|
||||||
return "NUMLOCK";
|
case 144:
|
||||||
case 145:
|
return "NUMLOCK";
|
||||||
return "SCRLOCK";
|
case 145:
|
||||||
case 182:
|
return "SCRLOCK";
|
||||||
return "COMP";
|
case 182:
|
||||||
case 183:
|
return "COMP";
|
||||||
return "CALC";
|
case 183:
|
||||||
case 186:
|
return "CALC";
|
||||||
return ";";
|
case 186:
|
||||||
case 187:
|
return ";";
|
||||||
return "+";
|
case 187:
|
||||||
case 188:
|
return "+";
|
||||||
return ",";
|
case 188:
|
||||||
case 189:
|
return ",";
|
||||||
return "-";
|
case 189:
|
||||||
case 191:
|
return "-";
|
||||||
return "/";
|
case 191:
|
||||||
case 219:
|
return "/";
|
||||||
return "[";
|
case 219:
|
||||||
case 220:
|
return "[";
|
||||||
return "\\";
|
case 220:
|
||||||
case 221:
|
return "\\";
|
||||||
return "]";
|
case 221:
|
||||||
case 222:
|
return "]";
|
||||||
return "'";
|
case 222:
|
||||||
}
|
return "'";
|
||||||
|
}
|
||||||
return String.fromCharCode(code);
|
|
||||||
}
|
return String.fromCharCode(code);
|
||||||
|
}
|
||||||
export class Keybinding {
|
|
||||||
/**
|
export class Keybinding {
|
||||||
*
|
/**
|
||||||
* @param {KeyActionMapper} keyMapper
|
*
|
||||||
* @param {Application} app
|
* @param {KeyActionMapper} keyMapper
|
||||||
* @param {object} param0
|
* @param {Application} app
|
||||||
* @param {number} param0.keyCode
|
* @param {object} param0
|
||||||
* @param {boolean=} param0.builtin
|
* @param {number} param0.keyCode
|
||||||
* @param {boolean=} param0.repeated
|
* @param {boolean=} param0.builtin
|
||||||
*/
|
* @param {boolean=} param0.repeated
|
||||||
constructor(keyMapper, app, { keyCode, builtin = false, repeated = false }) {
|
*/
|
||||||
assert(keyCode && Number.isInteger(keyCode), "Invalid key code: " + keyCode);
|
constructor(keyMapper, app, { keyCode, builtin = false, repeated = false }) {
|
||||||
this.keyMapper = keyMapper;
|
assert(keyCode && Number.isInteger(keyCode), "Invalid key code: " + keyCode);
|
||||||
this.app = app;
|
this.keyMapper = keyMapper;
|
||||||
this.keyCode = keyCode;
|
this.app = app;
|
||||||
this.builtin = builtin;
|
this.keyCode = keyCode;
|
||||||
this.repeated = repeated;
|
this.builtin = builtin;
|
||||||
|
this.repeated = repeated;
|
||||||
this.signal = new Signal();
|
|
||||||
this.toggled = new Signal();
|
this.signal = new Signal();
|
||||||
}
|
this.toggled = new Signal();
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Returns whether this binding is currently pressed
|
/**
|
||||||
* @returns {boolean}
|
* Returns whether this binding is currently pressed
|
||||||
*/
|
* @returns {boolean}
|
||||||
get pressed() {
|
*/
|
||||||
// Check if the key is down
|
get pressed() {
|
||||||
if (this.app.inputMgr.keysDown.has(this.keyCode)) {
|
// Check if the key is down
|
||||||
// Check if it is the top reciever
|
if (this.app.inputMgr.keysDown.has(this.keyCode)) {
|
||||||
const reciever = this.keyMapper.inputReceiver;
|
// Check if it is the top reciever
|
||||||
return this.app.inputMgr.getTopReciever() === reciever;
|
const reciever = this.keyMapper.inputReceiver;
|
||||||
}
|
return this.app.inputMgr.getTopReciever() === reciever;
|
||||||
return false;
|
}
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Adds an event listener
|
/**
|
||||||
* @param {function() : void} receiver
|
* Adds an event listener
|
||||||
* @param {object=} scope
|
* @param {function() : void} receiver
|
||||||
*/
|
* @param {object=} scope
|
||||||
add(receiver, scope = null) {
|
*/
|
||||||
this.signal.add(receiver, scope);
|
add(receiver, scope = null) {
|
||||||
}
|
this.signal.add(receiver, scope);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @param {Element} elem
|
/**
|
||||||
* @returns {HTMLElement} the created element, or null if the keybindings are not shown
|
* @param {Element} elem
|
||||||
* */
|
* @returns {HTMLElement} the created element, or null if the keybindings are not shown
|
||||||
appendLabelToElement(elem) {
|
* */
|
||||||
if (IS_MOBILE) {
|
appendLabelToElement(elem) {
|
||||||
return null;
|
if (IS_MOBILE) {
|
||||||
}
|
return null;
|
||||||
const spacer = document.createElement("code");
|
}
|
||||||
spacer.classList.add("keybinding");
|
const spacer = document.createElement("code");
|
||||||
spacer.innerHTML = getStringForKeyCode(this.keyCode);
|
spacer.classList.add("keybinding");
|
||||||
elem.appendChild(spacer);
|
spacer.innerHTML = getStringForKeyCode(this.keyCode);
|
||||||
return spacer;
|
elem.appendChild(spacer);
|
||||||
}
|
return spacer;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Returns the key code as a nice string
|
/**
|
||||||
*/
|
* Returns the key code as a nice string
|
||||||
getKeyCodeString() {
|
*/
|
||||||
return getStringForKeyCode(this.keyCode);
|
getKeyCodeString() {
|
||||||
}
|
return getStringForKeyCode(this.keyCode);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Remvoes all signal receivers
|
/**
|
||||||
*/
|
* Remvoes all signal receivers
|
||||||
clearSignalReceivers() {
|
*/
|
||||||
this.signal.removeAll();
|
clearSignalReceivers() {
|
||||||
}
|
this.signal.removeAll();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
export class KeyActionMapper {
|
|
||||||
/**
|
export class KeyActionMapper {
|
||||||
*
|
/**
|
||||||
* @param {GameRoot} root
|
*
|
||||||
* @param {InputReceiver} inputReciever
|
* @param {GameRoot} root
|
||||||
*/
|
* @param {InputReceiver} inputReciever
|
||||||
constructor(root, inputReciever) {
|
*/
|
||||||
this.root = root;
|
constructor(root, inputReciever) {
|
||||||
this.inputReceiver = inputReciever;
|
this.root = root;
|
||||||
|
this.inputReceiver = inputReciever;
|
||||||
inputReciever.keydown.add(this.handleKeydown, this);
|
|
||||||
inputReciever.keyup.add(this.handleKeyup, this);
|
inputReciever.keydown.add(this.handleKeydown, this);
|
||||||
|
inputReciever.keyup.add(this.handleKeyup, this);
|
||||||
/** @type {Object.<string, Keybinding>} */
|
|
||||||
this.keybindings = {};
|
/** @type {Object.<string, Keybinding>} */
|
||||||
|
this.keybindings = {};
|
||||||
const overrides = root.app.settings.getKeybindingOverrides();
|
|
||||||
|
const overrides = root.app.settings.getKeybindingOverrides();
|
||||||
for (const category in KEYMAPPINGS) {
|
|
||||||
for (const key in KEYMAPPINGS[category]) {
|
for (const category in KEYMAPPINGS) {
|
||||||
let payload = Object.assign({}, KEYMAPPINGS[category][key]);
|
for (const key in KEYMAPPINGS[category]) {
|
||||||
if (overrides[key]) {
|
let payload = Object.assign({}, KEYMAPPINGS[category][key]);
|
||||||
payload.keyCode = overrides[key];
|
if (overrides[key]) {
|
||||||
}
|
payload.keyCode = overrides[key];
|
||||||
|
}
|
||||||
this.keybindings[key] = new Keybinding(this, this.root.app, payload);
|
|
||||||
}
|
this.keybindings[key] = new Keybinding(this, this.root.app, payload);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
inputReciever.pageBlur.add(this.onPageBlur, this);
|
|
||||||
inputReciever.destroyed.add(this.cleanup, this);
|
inputReciever.pageBlur.add(this.onPageBlur, this);
|
||||||
}
|
inputReciever.destroyed.add(this.cleanup, this);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Returns all keybindings starting with the given id
|
/**
|
||||||
* @param {string} pattern
|
* Returns all keybindings starting with the given id
|
||||||
* @returns {Array<Keybinding>}
|
* @param {string} pattern
|
||||||
*/
|
* @returns {Array<Keybinding>}
|
||||||
getKeybindingsStartingWith(pattern) {
|
*/
|
||||||
let result = [];
|
getKeybindingsStartingWith(pattern) {
|
||||||
for (const key in this.keybindings) {
|
let result = [];
|
||||||
if (key.startsWith(pattern)) {
|
for (const key in this.keybindings) {
|
||||||
result.push(this.keybindings[key]);
|
if (key.startsWith(pattern)) {
|
||||||
}
|
result.push(this.keybindings[key]);
|
||||||
}
|
}
|
||||||
return result;
|
}
|
||||||
}
|
return result;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Forwards the given events to the other mapper (used in tooltips)
|
/**
|
||||||
* @param {KeyActionMapper} receiver
|
* Forwards the given events to the other mapper (used in tooltips)
|
||||||
* @param {Array<string>} bindings
|
* @param {KeyActionMapper} receiver
|
||||||
*/
|
* @param {Array<string>} bindings
|
||||||
forward(receiver, bindings) {
|
*/
|
||||||
for (let i = 0; i < bindings.length; ++i) {
|
forward(receiver, bindings) {
|
||||||
const key = bindings[i];
|
for (let i = 0; i < bindings.length; ++i) {
|
||||||
this.keybindings[key].signal.add((...args) => receiver.keybindings[key].signal.dispatch(...args));
|
const key = bindings[i];
|
||||||
}
|
this.keybindings[key].signal.add((...args) => receiver.keybindings[key].signal.dispatch(...args));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cleanup() {
|
|
||||||
for (const key in this.keybindings) {
|
cleanup() {
|
||||||
this.keybindings[key].signal.removeAll();
|
for (const key in this.keybindings) {
|
||||||
}
|
this.keybindings[key].signal.removeAll();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
onPageBlur() {
|
|
||||||
// Reset all down states
|
onPageBlur() {
|
||||||
// Find mapping
|
// Reset all down states
|
||||||
for (const key in this.keybindings) {
|
// Find mapping
|
||||||
/** @type {Keybinding} */
|
for (const key in this.keybindings) {
|
||||||
const binding = this.keybindings[key];
|
/** @type {Keybinding} */
|
||||||
}
|
const binding = this.keybindings[key];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Internal keydown handler
|
/**
|
||||||
* @param {object} param0
|
* Internal keydown handler
|
||||||
* @param {number} param0.keyCode
|
* @param {object} param0
|
||||||
* @param {boolean} param0.shift
|
* @param {number} param0.keyCode
|
||||||
* @param {boolean} param0.alt
|
* @param {boolean} param0.shift
|
||||||
* @param {boolean=} param0.initial
|
* @param {boolean} param0.alt
|
||||||
*/
|
* @param {boolean=} param0.initial
|
||||||
handleKeydown({ keyCode, shift, alt, initial }) {
|
*/
|
||||||
let stop = false;
|
handleKeydown({ keyCode, shift, alt, initial }) {
|
||||||
|
let stop = false;
|
||||||
// Find mapping
|
|
||||||
for (const key in this.keybindings) {
|
// Find mapping
|
||||||
/** @type {Keybinding} */
|
for (const key in this.keybindings) {
|
||||||
const binding = this.keybindings[key];
|
/** @type {Keybinding} */
|
||||||
if (binding.keyCode === keyCode && (initial || binding.repeated)) {
|
const binding = this.keybindings[key];
|
||||||
/** @type {Signal} */
|
if (binding.keyCode === keyCode && (initial || binding.repeated)) {
|
||||||
const signal = this.keybindings[key].signal;
|
/** @type {Signal} */
|
||||||
if (signal.dispatch() === STOP_PROPAGATION) {
|
const signal = this.keybindings[key].signal;
|
||||||
return;
|
if (signal.dispatch() === STOP_PROPAGATION) {
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (stop) {
|
|
||||||
return STOP_PROPAGATION;
|
if (stop) {
|
||||||
}
|
return STOP_PROPAGATION;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Internal keyup handler
|
/**
|
||||||
* @param {object} param0
|
* Internal keyup handler
|
||||||
* @param {number} param0.keyCode
|
* @param {object} param0
|
||||||
* @param {boolean} param0.shift
|
* @param {number} param0.keyCode
|
||||||
* @param {boolean} param0.alt
|
* @param {boolean} param0.shift
|
||||||
*/
|
* @param {boolean} param0.alt
|
||||||
handleKeyup({ keyCode, shift, alt }) {
|
*/
|
||||||
// Empty
|
handleKeyup({ keyCode, shift, alt }) {
|
||||||
}
|
// Empty
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Returns a given keybinding
|
/**
|
||||||
* @param {{ keyCode: number }} binding
|
* Returns a given keybinding
|
||||||
* @returns {Keybinding}
|
* @param {{ keyCode: number }} binding
|
||||||
*/
|
* @returns {Keybinding}
|
||||||
getBinding(binding) {
|
*/
|
||||||
// @ts-ignore
|
getBinding(binding) {
|
||||||
const id = binding.id;
|
// @ts-ignore
|
||||||
assert(id, "Not a valid keybinding: " + JSON.stringify(binding));
|
const id = binding.id;
|
||||||
assert(this.keybindings[id], "Keybinding " + id + " not known!");
|
assert(id, "Not a valid keybinding: " + JSON.stringify(binding));
|
||||||
return this.keybindings[id];
|
assert(this.keybindings[id], "Keybinding " + id + " not known!");
|
||||||
}
|
return this.keybindings[id];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -52,10 +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.display.drawChunk(parameters, this);
|
||||||
|
systems.storage.drawChunk(parameters, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,11 +103,9 @@ export class MapChunkView extends MapChunk {
|
|||||||
|
|
||||||
const destX = this.x * dims + patch.pos.x * globalConfig.tileSize;
|
const destX = this.x * dims + patch.pos.x * globalConfig.tileSize;
|
||||||
const destY = this.y * dims + patch.pos.y * globalConfig.tileSize;
|
const destY = this.y * dims + patch.pos.y * globalConfig.tileSize;
|
||||||
const destSize = Math.min(80, 30 / parameters.zoomLevel);
|
const diameter = Math.min(80, 30 / parameters.zoomLevel);
|
||||||
|
|
||||||
if (parameters.visibleRect.containsCircle(destX, destY, destSize)) {
|
patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter);
|
||||||
patch.item.drawCentered(destX, destY, parameters, destSize);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,162 +1,171 @@
|
|||||||
import { gMetaBuildingRegistry } from "../core/global_registries";
|
import { gMetaBuildingRegistry } from "../core/global_registries";
|
||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
import { MetaBeltBuilding } from "./buildings/belt";
|
import { MetaBeltBuilding } from "./buildings/belt";
|
||||||
import { MetaBeltBaseBuilding } from "./buildings/belt_base";
|
import { MetaBeltBaseBuilding } from "./buildings/belt_base";
|
||||||
import { enumCutterVariants, MetaCutterBuilding } from "./buildings/cutter";
|
import { enumCutterVariants, MetaCutterBuilding } from "./buildings/cutter";
|
||||||
import { MetaHubBuilding } from "./buildings/hub";
|
import { MetaHubBuilding } from "./buildings/hub";
|
||||||
import { enumMinerVariants, MetaMinerBuilding } from "./buildings/miner";
|
import { enumMinerVariants, MetaMinerBuilding } from "./buildings/miner";
|
||||||
import { MetaMixerBuilding } from "./buildings/mixer";
|
import { MetaMixerBuilding } from "./buildings/mixer";
|
||||||
import { enumPainterVariants, MetaPainterBuilding } from "./buildings/painter";
|
import { enumPainterVariants, MetaPainterBuilding } from "./buildings/painter";
|
||||||
import { enumRotaterVariants, MetaRotaterBuilding } from "./buildings/rotater";
|
import { enumRotaterVariants, MetaRotaterBuilding } from "./buildings/rotater";
|
||||||
import { enumSplitterVariants, MetaSplitterBuilding } from "./buildings/splitter";
|
import { enumSplitterVariants, MetaSplitterBuilding } from "./buildings/splitter";
|
||||||
import { MetaStackerBuilding } from "./buildings/stacker";
|
import { MetaStackerBuilding } from "./buildings/stacker";
|
||||||
import { enumTrashVariants, MetaTrashBuilding } from "./buildings/trash";
|
import { enumTrashVariants, MetaTrashBuilding } from "./buildings/trash";
|
||||||
import { enumUndergroundBeltVariants, MetaUndergroundBeltBuilding } from "./buildings/underground_belt";
|
import { enumUndergroundBeltVariants, MetaUndergroundBeltBuilding } from "./buildings/underground_belt";
|
||||||
import { MetaWireBuilding } from "./buildings/wire";
|
import { MetaWireBuilding } from "./buildings/wire";
|
||||||
import { gBuildingVariants, registerBuildingVariant } from "./building_codes";
|
import { gBuildingVariants, registerBuildingVariant } from "./building_codes";
|
||||||
import { defaultBuildingVariant } from "./meta_building";
|
import { defaultBuildingVariant } from "./meta_building";
|
||||||
import { MetaConstantSignalBuilding } from "./buildings/constant_signal";
|
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, enumWireTunnelVariants } from "./buildings/wire_tunnel";
|
import { MetaWireTunnelBuilding, enumWireTunnelVariants } from "./buildings/wire_tunnel";
|
||||||
import { MetaDisplayBuilding } from "./buildings/display";
|
import { MetaDisplayBuilding } from "./buildings/display";
|
||||||
|
import { MetaVirtualProcessorBuilding, enumVirtualProcessorVariants } from "./buildings/virtual_processor";
|
||||||
const logger = createLogger("building_registry");
|
|
||||||
|
const logger = createLogger("building_registry");
|
||||||
export function initMetaBuildingRegistry() {
|
|
||||||
gMetaBuildingRegistry.register(MetaSplitterBuilding);
|
export function initMetaBuildingRegistry() {
|
||||||
gMetaBuildingRegistry.register(MetaMinerBuilding);
|
gMetaBuildingRegistry.register(MetaSplitterBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaCutterBuilding);
|
gMetaBuildingRegistry.register(MetaMinerBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaRotaterBuilding);
|
gMetaBuildingRegistry.register(MetaCutterBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaStackerBuilding);
|
gMetaBuildingRegistry.register(MetaRotaterBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaMixerBuilding);
|
gMetaBuildingRegistry.register(MetaStackerBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaPainterBuilding);
|
gMetaBuildingRegistry.register(MetaMixerBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaTrashBuilding);
|
gMetaBuildingRegistry.register(MetaPainterBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaBeltBuilding);
|
gMetaBuildingRegistry.register(MetaTrashBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaUndergroundBeltBuilding);
|
gMetaBuildingRegistry.register(MetaBeltBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaHubBuilding);
|
gMetaBuildingRegistry.register(MetaUndergroundBeltBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaWireBuilding);
|
gMetaBuildingRegistry.register(MetaHubBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaConstantSignalBuilding);
|
gMetaBuildingRegistry.register(MetaWireBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaLogicGateBuilding);
|
gMetaBuildingRegistry.register(MetaConstantSignalBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaLeverBuilding);
|
gMetaBuildingRegistry.register(MetaLogicGateBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaFilterBuilding);
|
gMetaBuildingRegistry.register(MetaLeverBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaWireTunnelBuilding);
|
gMetaBuildingRegistry.register(MetaFilterBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaDisplayBuilding);
|
gMetaBuildingRegistry.register(MetaWireTunnelBuilding);
|
||||||
|
gMetaBuildingRegistry.register(MetaDisplayBuilding);
|
||||||
// Belt
|
gMetaBuildingRegistry.register(MetaVirtualProcessorBuilding);
|
||||||
registerBuildingVariant(1, MetaBeltBaseBuilding, defaultBuildingVariant, 0);
|
|
||||||
registerBuildingVariant(2, MetaBeltBaseBuilding, defaultBuildingVariant, 1);
|
// Belt
|
||||||
registerBuildingVariant(3, MetaBeltBaseBuilding, defaultBuildingVariant, 2);
|
registerBuildingVariant(1, MetaBeltBaseBuilding, defaultBuildingVariant, 0);
|
||||||
|
registerBuildingVariant(2, MetaBeltBaseBuilding, defaultBuildingVariant, 1);
|
||||||
// Splitter
|
registerBuildingVariant(3, MetaBeltBaseBuilding, defaultBuildingVariant, 2);
|
||||||
registerBuildingVariant(4, MetaSplitterBuilding);
|
|
||||||
registerBuildingVariant(5, MetaSplitterBuilding, enumSplitterVariants.compact);
|
// Splitter
|
||||||
registerBuildingVariant(6, MetaSplitterBuilding, enumSplitterVariants.compactInverse);
|
registerBuildingVariant(4, MetaSplitterBuilding);
|
||||||
|
registerBuildingVariant(5, MetaSplitterBuilding, enumSplitterVariants.compact);
|
||||||
// Miner
|
registerBuildingVariant(6, MetaSplitterBuilding, enumSplitterVariants.compactInverse);
|
||||||
registerBuildingVariant(7, MetaMinerBuilding);
|
|
||||||
registerBuildingVariant(8, MetaMinerBuilding, enumMinerVariants.chainable);
|
// Miner
|
||||||
|
registerBuildingVariant(7, MetaMinerBuilding);
|
||||||
// Cutter
|
registerBuildingVariant(8, MetaMinerBuilding, enumMinerVariants.chainable);
|
||||||
registerBuildingVariant(9, MetaCutterBuilding);
|
|
||||||
registerBuildingVariant(10, MetaCutterBuilding, enumCutterVariants.quad);
|
// Cutter
|
||||||
|
registerBuildingVariant(9, MetaCutterBuilding);
|
||||||
// Rotater
|
registerBuildingVariant(10, MetaCutterBuilding, enumCutterVariants.quad);
|
||||||
registerBuildingVariant(11, MetaRotaterBuilding);
|
|
||||||
registerBuildingVariant(12, MetaRotaterBuilding, enumRotaterVariants.ccw);
|
// Rotater
|
||||||
registerBuildingVariant(13, MetaRotaterBuilding, enumRotaterVariants.fl);
|
registerBuildingVariant(11, MetaRotaterBuilding);
|
||||||
|
registerBuildingVariant(12, MetaRotaterBuilding, enumRotaterVariants.ccw);
|
||||||
// Stacker
|
registerBuildingVariant(13, MetaRotaterBuilding, enumRotaterVariants.fl);
|
||||||
registerBuildingVariant(14, MetaStackerBuilding);
|
|
||||||
|
// Stacker
|
||||||
// Mixer
|
registerBuildingVariant(14, MetaStackerBuilding);
|
||||||
registerBuildingVariant(15, MetaMixerBuilding);
|
|
||||||
|
// Mixer
|
||||||
// Painter
|
registerBuildingVariant(15, MetaMixerBuilding);
|
||||||
registerBuildingVariant(16, MetaPainterBuilding);
|
|
||||||
registerBuildingVariant(17, MetaPainterBuilding, enumPainterVariants.mirrored);
|
// Painter
|
||||||
registerBuildingVariant(18, MetaPainterBuilding, enumPainterVariants.double);
|
registerBuildingVariant(16, MetaPainterBuilding);
|
||||||
registerBuildingVariant(19, MetaPainterBuilding, enumPainterVariants.quad);
|
registerBuildingVariant(17, MetaPainterBuilding, enumPainterVariants.mirrored);
|
||||||
|
registerBuildingVariant(18, MetaPainterBuilding, enumPainterVariants.double);
|
||||||
// Trash
|
registerBuildingVariant(19, MetaPainterBuilding, enumPainterVariants.quad);
|
||||||
registerBuildingVariant(20, MetaTrashBuilding);
|
|
||||||
registerBuildingVariant(21, MetaTrashBuilding, enumTrashVariants.storage);
|
// Trash
|
||||||
|
registerBuildingVariant(20, MetaTrashBuilding);
|
||||||
// Underground belt
|
registerBuildingVariant(21, MetaTrashBuilding, enumTrashVariants.storage);
|
||||||
registerBuildingVariant(22, MetaUndergroundBeltBuilding, defaultBuildingVariant, 0);
|
|
||||||
registerBuildingVariant(23, MetaUndergroundBeltBuilding, defaultBuildingVariant, 1);
|
// Underground belt
|
||||||
registerBuildingVariant(24, MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2, 0);
|
registerBuildingVariant(22, MetaUndergroundBeltBuilding, defaultBuildingVariant, 0);
|
||||||
registerBuildingVariant(25, MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2, 1);
|
registerBuildingVariant(23, MetaUndergroundBeltBuilding, defaultBuildingVariant, 1);
|
||||||
|
registerBuildingVariant(24, MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2, 0);
|
||||||
// Hub
|
registerBuildingVariant(25, MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2, 1);
|
||||||
registerBuildingVariant(26, MetaHubBuilding);
|
|
||||||
|
// Hub
|
||||||
// Wire
|
registerBuildingVariant(26, MetaHubBuilding);
|
||||||
registerBuildingVariant(27, MetaWireBuilding, defaultBuildingVariant, 0);
|
|
||||||
registerBuildingVariant(28, MetaWireBuilding, defaultBuildingVariant, 1);
|
// Wire
|
||||||
registerBuildingVariant(29, MetaWireBuilding, defaultBuildingVariant, 2);
|
registerBuildingVariant(27, MetaWireBuilding, defaultBuildingVariant, 0);
|
||||||
registerBuildingVariant(30, MetaWireBuilding, defaultBuildingVariant, 3);
|
registerBuildingVariant(28, MetaWireBuilding, defaultBuildingVariant, 1);
|
||||||
|
registerBuildingVariant(29, MetaWireBuilding, defaultBuildingVariant, 2);
|
||||||
// Constant signal
|
registerBuildingVariant(30, MetaWireBuilding, defaultBuildingVariant, 3);
|
||||||
registerBuildingVariant(31, MetaConstantSignalBuilding);
|
|
||||||
|
// Constant signal
|
||||||
// Logic gate
|
registerBuildingVariant(31, MetaConstantSignalBuilding);
|
||||||
registerBuildingVariant(32, MetaLogicGateBuilding);
|
|
||||||
registerBuildingVariant(34, MetaLogicGateBuilding, enumLogicGateVariants.not);
|
// Logic gate
|
||||||
registerBuildingVariant(35, MetaLogicGateBuilding, enumLogicGateVariants.xor);
|
registerBuildingVariant(32, MetaLogicGateBuilding);
|
||||||
registerBuildingVariant(36, MetaLogicGateBuilding, enumLogicGateVariants.or);
|
registerBuildingVariant(34, MetaLogicGateBuilding, enumLogicGateVariants.not);
|
||||||
registerBuildingVariant(38, MetaLogicGateBuilding, enumLogicGateVariants.transistor);
|
registerBuildingVariant(35, MetaLogicGateBuilding, enumLogicGateVariants.xor);
|
||||||
|
registerBuildingVariant(36, MetaLogicGateBuilding, enumLogicGateVariants.or);
|
||||||
// Lever
|
registerBuildingVariant(38, MetaLogicGateBuilding, enumLogicGateVariants.transistor);
|
||||||
registerBuildingVariant(33, MetaLeverBuilding);
|
|
||||||
|
// Lever
|
||||||
// Filter
|
registerBuildingVariant(33, MetaLeverBuilding);
|
||||||
registerBuildingVariant(37, MetaFilterBuilding);
|
|
||||||
|
// Filter
|
||||||
// Wire tunnel
|
registerBuildingVariant(37, MetaFilterBuilding);
|
||||||
registerBuildingVariant(39, MetaWireTunnelBuilding);
|
|
||||||
registerBuildingVariant(41, MetaWireTunnelBuilding, enumWireTunnelVariants.coating);
|
// Wire tunnel
|
||||||
|
registerBuildingVariant(39, MetaWireTunnelBuilding);
|
||||||
// Display
|
registerBuildingVariant(41, MetaWireTunnelBuilding, enumWireTunnelVariants.coating);
|
||||||
registerBuildingVariant(40, MetaDisplayBuilding);
|
|
||||||
|
// Display
|
||||||
// Propagate instances
|
registerBuildingVariant(40, MetaDisplayBuilding);
|
||||||
for (const key in gBuildingVariants) {
|
|
||||||
gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass(
|
// Virtual Processor
|
||||||
gBuildingVariants[key].metaClass
|
registerBuildingVariant(42, MetaVirtualProcessorBuilding);
|
||||||
);
|
registerBuildingVariant(43, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.analyzer);
|
||||||
}
|
registerBuildingVariant(44, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.rotater);
|
||||||
|
registerBuildingVariant(45, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.unstacker);
|
||||||
for (const key in gBuildingVariants) {
|
registerBuildingVariant(46, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.shapecompare);
|
||||||
const variant = gBuildingVariants[key];
|
|
||||||
assert(variant.metaClass, "Variant has no meta: " + key);
|
// Propagate instances
|
||||||
|
for (const key in gBuildingVariants) {
|
||||||
if (typeof variant.rotationVariant === "undefined") {
|
gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass(
|
||||||
variant.rotationVariant = 0;
|
gBuildingVariants[key].metaClass
|
||||||
}
|
);
|
||||||
if (typeof variant.variant === "undefined") {
|
}
|
||||||
variant.variant = defaultBuildingVariant;
|
|
||||||
}
|
for (const key in gBuildingVariants) {
|
||||||
}
|
const variant = gBuildingVariants[key];
|
||||||
|
assert(variant.metaClass, "Variant has no meta: " + key);
|
||||||
logger.log("Registered", gMetaBuildingRegistry.getNumEntries(), "buildings");
|
|
||||||
logger.log("Registered", Object.keys(gBuildingVariants).length, "building codes");
|
if (typeof variant.rotationVariant === "undefined") {
|
||||||
}
|
variant.rotationVariant = 0;
|
||||||
|
}
|
||||||
/**
|
if (typeof variant.variant === "undefined") {
|
||||||
* Once all sprites are loaded, propagates the cache
|
variant.variant = defaultBuildingVariant;
|
||||||
*/
|
}
|
||||||
export function initBuildingCodesAfterResourcesLoaded() {
|
}
|
||||||
logger.log("Propagating sprite cache");
|
|
||||||
for (const key in gBuildingVariants) {
|
logger.log("Registered", gMetaBuildingRegistry.getNumEntries(), "buildings");
|
||||||
const variant = gBuildingVariants[key];
|
logger.log("Registered", Object.keys(gBuildingVariants).length, "building codes");
|
||||||
|
}
|
||||||
variant.sprite = variant.metaInstance.getSprite(variant.rotationVariant, variant.variant);
|
|
||||||
variant.blueprintSprite = variant.metaInstance.getBlueprintSprite(
|
/**
|
||||||
variant.rotationVariant,
|
* Once all sprites are loaded, propagates the cache
|
||||||
variant.variant
|
*/
|
||||||
);
|
export function initBuildingCodesAfterResourcesLoaded() {
|
||||||
variant.silhouetteColor = variant.metaInstance.getSilhouetteColor();
|
logger.log("Propagating sprite cache");
|
||||||
}
|
for (const key in gBuildingVariants) {
|
||||||
}
|
const variant = gBuildingVariants[key];
|
||||||
|
|
||||||
|
variant.sprite = variant.metaInstance.getSprite(variant.rotationVariant, variant.variant);
|
||||||
|
variant.blueprintSprite = variant.metaInstance.getBlueprintSprite(
|
||||||
|
variant.rotationVariant,
|
||||||
|
variant.variant
|
||||||
|
);
|
||||||
|
variant.silhouetteColor = variant.metaInstance.getSilhouetteColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -149,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()),
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ import { GameSystemWithFilter } from "../game_system_with_filter";
|
|||||||
import { MapChunkView } from "../map_chunk_view";
|
import { MapChunkView } from "../map_chunk_view";
|
||||||
import { defaultBuildingVariant } from "../meta_building";
|
import { defaultBuildingVariant } from "../meta_building";
|
||||||
import { getCodeFromBuildingData } from "../building_codes";
|
import { getCodeFromBuildingData } from "../building_codes";
|
||||||
import { enumLayer } from "../root";
|
|
||||||
|
|
||||||
export const BELT_ANIM_COUNT = 14;
|
export const BELT_ANIM_COUNT = 14;
|
||||||
|
|
||||||
@ -496,13 +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.containedEntitiesByLayer[enumLayer.regular];
|
const contents = chunk.containedEntitiesByLayer.regular;
|
||||||
for (let i = 0; i < contents.length; ++i) {
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
const entity = contents[i];
|
const entity = contents[i];
|
||||||
if (entity.components.Belt) {
|
if (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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ 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 { MapChunkView } from "../map_chunk_view";
|
||||||
import { DrawParameters } from "../../core/draw_parameters";
|
import { DrawParameters } from "../../core/draw_parameters";
|
||||||
import { enumLayer } from "../root";
|
|
||||||
|
|
||||||
export class BeltUnderlaysSystem extends GameSystemWithFilter {
|
export class BeltUnderlaysSystem extends GameSystemWithFilter {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
@ -29,38 +28,56 @@ export class BeltUnderlaysSystem extends GameSystemWithFilter {
|
|||||||
// 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[enumLayer.regular];
|
const contents = chunk.containedEntitiesByLayer.regular;
|
||||||
for (let i = 0; i < contents.length; ++i) {
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
const entity = contents[i];
|
const entity = contents[i];
|
||||||
const underlayComp = entity.components.BeltUnderlays;
|
const underlayComp = entity.components.BeltUnderlays;
|
||||||
if (underlayComp) {
|
if (!underlayComp) {
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
continue;
|
||||||
const underlays = underlayComp.underlays;
|
}
|
||||||
for (let i = 0; i < underlays.length; ++i) {
|
|
||||||
const { pos, direction } = underlays[i];
|
|
||||||
const transformedPos = staticComp.localTileToWorld(pos);
|
|
||||||
|
|
||||||
if (!chunk.tileSpaceRectangle.containsPoint(transformedPos.x, transformedPos.y)) {
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
continue;
|
const underlays = underlayComp.underlays;
|
||||||
}
|
for (let i = 0; i < underlays.length; ++i) {
|
||||||
|
const { pos, direction } = underlays[i];
|
||||||
|
const transformedPos = staticComp.localTileToWorld(pos);
|
||||||
|
|
||||||
const angle = enumDirectionToAngle[staticComp.localDirectionToWorld(direction)];
|
// Culling
|
||||||
|
if (!chunk.tileSpaceRectangle.containsPoint(transformedPos.x, transformedPos.y)) {
|
||||||
// SYNC with systems/belt.js:drawSingleEntity!
|
continue;
|
||||||
const animationIndex = Math.floor(
|
|
||||||
((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) *
|
|
||||||
globalConfig.itemSpacingOnBelts
|
|
||||||
);
|
|
||||||
|
|
||||||
drawRotatedSprite({
|
|
||||||
parameters,
|
|
||||||
sprite: this.underlayBeltSprites[animationIndex % this.underlayBeltSprites.length],
|
|
||||||
x: (transformedPos.x + 0.5) * globalConfig.tileSize,
|
|
||||||
y: (transformedPos.y + 0.5) * globalConfig.tileSize,
|
|
||||||
angle: Math.radians(angle),
|
|
||||||
size: globalConfig.tileSize,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)];
|
||||||
|
|
||||||
|
// SYNC with systems/belt.js:drawSingleEntity!
|
||||||
|
const animationIndex = Math.floor(
|
||||||
|
((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) *
|
||||||
|
globalConfig.itemSpacingOnBelts
|
||||||
|
);
|
||||||
|
|
||||||
|
drawRotatedSprite({
|
||||||
|
parameters,
|
||||||
|
sprite: this.underlayBeltSprites[animationIndex % this.underlayBeltSprites.length],
|
||||||
|
x: destX + globalConfig.halfTileSize,
|
||||||
|
y: destY + globalConfig.halfTileSize,
|
||||||
|
angle: Math.radians(angle),
|
||||||
|
size: globalConfig.tileSize,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,9 +66,11 @@ export class DisplaySystem extends GameSystemWithFilter {
|
|||||||
if (entity && entity.components.Display) {
|
if (entity && entity.components.Display) {
|
||||||
const pinsComp = entity.components.WiredPins;
|
const pinsComp = entity.components.WiredPins;
|
||||||
const network = pinsComp.slots[0].linkedNetwork;
|
const network = pinsComp.slots[0].linkedNetwork;
|
||||||
|
|
||||||
if (!network || !network.currentValue) {
|
if (!network || !network.currentValue) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = this.getDisplayItem(network.currentValue);
|
const value = this.getDisplayItem(network.currentValue);
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
@ -84,7 +86,7 @@ export class DisplaySystem extends GameSystemWithFilter {
|
|||||||
globalConfig.tileSize
|
globalConfig.tileSize
|
||||||
);
|
);
|
||||||
} else if (value.getItemType() === "shape") {
|
} else if (value.getItemType() === "shape") {
|
||||||
value.draw(
|
value.drawItemCenteredClipped(
|
||||||
(origin.x + 0.5) * globalConfig.tileSize,
|
(origin.x + 0.5) * globalConfig.tileSize,
|
||||||
(origin.y + 0.5) * globalConfig.tileSize,
|
(origin.y + 0.5) * globalConfig.tileSize,
|
||||||
parameters,
|
parameters,
|
||||||
|
@ -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.drawCentered(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,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +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 { MapChunkView } from "../map_chunk_view";
|
||||||
|
|
||||||
export class ItemAcceptorSystem extends GameSystemWithFilter {
|
export class ItemAcceptorSystem extends GameSystemWithFilter {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
@ -38,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];
|
||||||
|
const acceptorComp = entity.components.ItemAcceptor;
|
||||||
|
if (!acceptorComp) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
* @param {DrawParameters} parameters
|
for (let animIndex = 0; animIndex < acceptorComp.itemConsumptionAnimations.length; ++animIndex) {
|
||||||
* @param {Entity} entity
|
const { item, slotIndex, animProgress, direction } = acceptorComp.itemConsumptionAnimations[
|
||||||
*/
|
animIndex
|
||||||
drawEntityRegularLayer(parameters, entity) {
|
];
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
|
||||||
const acceptorComp = entity.components.ItemAcceptor;
|
|
||||||
|
|
||||||
if (!staticComp.shouldBeDrawn(parameters)) {
|
const slotData = acceptorComp.slots[slotIndex];
|
||||||
return;
|
const realSlotPos = staticComp.localTileToWorld(slotData.pos);
|
||||||
}
|
|
||||||
|
|
||||||
for (let animIndex = 0; animIndex < acceptorComp.itemConsumptionAnimations.length; ++animIndex) {
|
if (!chunk.tileSpaceRectangle.containsPoint(realSlotPos.x, realSlotPos.y)) {
|
||||||
const { item, slotIndex, animProgress, direction } = acceptorComp.itemConsumptionAnimations[
|
// Not within this chunk
|
||||||
animIndex
|
continue;
|
||||||
];
|
}
|
||||||
|
|
||||||
const slotData = acceptorComp.slots[slotIndex];
|
const fadeOutDirection = enumDirectionToVector[staticComp.localDirectionToWorld(direction)];
|
||||||
|
const finalTile = realSlotPos.subScalars(
|
||||||
|
fadeOutDirection.x * (animProgress / 2 - 0.5),
|
||||||
|
fadeOutDirection.y * (animProgress / 2 - 0.5)
|
||||||
|
);
|
||||||
|
|
||||||
const slotWorldPos = staticComp.applyRotationToVector(slotData.pos).add(staticComp.origin);
|
item.drawItemCenteredClipped(
|
||||||
const fadeOutDirection = enumDirectionToVector[staticComp.localDirectionToWorld(direction)];
|
(finalTile.x + 0.5) * globalConfig.tileSize,
|
||||||
const finalTile = slotWorldPos.subScalars(
|
(finalTile.y + 0.5) * globalConfig.tileSize,
|
||||||
fadeOutDirection.x * (animProgress / 2 - 0.5),
|
parameters,
|
||||||
fadeOutDirection.y * (animProgress / 2 - 0.5)
|
globalConfig.defaultItemDiameter
|
||||||
);
|
);
|
||||||
item.drawCentered(
|
}
|
||||||
(finalTile.x + 0.5) * globalConfig.tileSize,
|
|
||||||
(finalTile.y + 0.5) * globalConfig.tileSize,
|
|
||||||
parameters
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import { ItemEjectorComponent } from "../components/item_ejector";
|
|||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
import { enumItemProcessorTypes } from "../components/item_processor";
|
import { enumItemProcessorTypes } from "../components/item_processor";
|
||||||
|
import { MapChunkView } from "../map_chunk_view";
|
||||||
|
|
||||||
const logger = createLogger("systems/ejector");
|
const logger = createLogger("systems/ejector");
|
||||||
|
|
||||||
@ -336,50 +337,52 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws everything
|
|
||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
|
* @param {MapChunkView} chunk
|
||||||
*/
|
*/
|
||||||
draw(parameters) {
|
drawChunk(parameters, chunk) {
|
||||||
this.forEachMatchingEntityOnScreen(parameters, this.drawSingleEntity.bind(this));
|
const contents = chunk.containedEntitiesByLayer.regular;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
* @param {DrawParameters} parameters
|
const entity = contents[i];
|
||||||
* @param {Entity} entity
|
const ejectorComp = entity.components.ItemEjector;
|
||||||
*/
|
if (!ejectorComp) {
|
||||||
drawSingleEntity(parameters, entity) {
|
|
||||||
const ejectorComp = entity.components.ItemEjector;
|
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
|
||||||
|
|
||||||
if (!staticComp.shouldBeDrawn(parameters)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < ejectorComp.slots.length; ++i) {
|
|
||||||
const slot = ejectorComp.slots[i];
|
|
||||||
const ejectedItem = slot.item;
|
|
||||||
|
|
||||||
if (!ejectedItem) {
|
|
||||||
// No item
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const realPosition = slot.pos.rotateFastMultipleOf90(staticComp.rotation);
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
const realDirection = Vector.transformDirectionFromMultipleOf90(
|
|
||||||
slot.direction,
|
|
||||||
staticComp.rotation
|
|
||||||
);
|
|
||||||
const realDirectionVector = enumDirectionToVector[realDirection];
|
|
||||||
|
|
||||||
const tileX =
|
for (let i = 0; i < ejectorComp.slots.length; ++i) {
|
||||||
staticComp.origin.x + realPosition.x + 0.5 + realDirectionVector.x * 0.5 * slot.progress;
|
const slot = ejectorComp.slots[i];
|
||||||
const tileY =
|
const ejectedItem = slot.item;
|
||||||
staticComp.origin.y + realPosition.y + 0.5 + realDirectionVector.y * 0.5 * slot.progress;
|
|
||||||
|
|
||||||
const worldX = tileX * globalConfig.tileSize;
|
if (!ejectedItem) {
|
||||||
const worldY = tileY * globalConfig.tileSize;
|
// No item
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ejectedItem.drawCentered(worldX, worldY, parameters);
|
const realPosition = staticComp.localTileToWorld(slot.pos);
|
||||||
|
if (!chunk.tileSpaceRectangle.containsPoint(realPosition.x, realPosition.y)) {
|
||||||
|
// Not within this chunk
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const realDirection = staticComp.localDirectionToWorld(slot.direction);
|
||||||
|
const realDirectionVector = enumDirectionToVector[realDirection];
|
||||||
|
|
||||||
|
const tileX = realPosition.x + 0.5 + realDirectionVector.x * 0.5 * slot.progress;
|
||||||
|
const tileY = realPosition.y + 0.5 + realDirectionVector.y * 0.5 * slot.progress;
|
||||||
|
|
||||||
|
const worldX = tileX * globalConfig.tileSize;
|
||||||
|
const worldY = tileY * globalConfig.tileSize;
|
||||||
|
|
||||||
|
ejectedItem.drawItemCenteredClipped(
|
||||||
|
worldX,
|
||||||
|
worldY,
|
||||||
|
parameters,
|
||||||
|
globalConfig.defaultItemDiameter
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,9 @@ export class LeverSystem extends GameSystemWithFilter {
|
|||||||
const contents = chunk.containedEntitiesByLayer.regular;
|
const contents = chunk.containedEntitiesByLayer.regular;
|
||||||
for (let i = 0; i < contents.length; ++i) {
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
const entity = contents[i];
|
const entity = contents[i];
|
||||||
if (entity && entity.components.Lever) {
|
const leverComp = entity.components.Lever;
|
||||||
const sprite = entity.components.Lever.toggled ? this.spriteOn : this.spriteOff;
|
if (leverComp) {
|
||||||
|
const sprite = leverComp.toggled ? this.spriteOn : this.spriteOff;
|
||||||
const origin = entity.components.StaticMapEntity.origin;
|
const origin = entity.components.StaticMapEntity.origin;
|
||||||
sprite.drawCached(
|
sprite.drawCached(
|
||||||
parameters,
|
parameters,
|
||||||
|
@ -1,180 +1,326 @@
|
|||||||
import { LogicGateComponent, enumLogicGateType } from "../components/logic_gate";
|
import { BaseItem } from "../base_item";
|
||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { enumColors } from "../colors";
|
||||||
import { BaseItem } from "../base_item";
|
import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate";
|
||||||
import { enumPinSlotType } from "../components/wired_pins";
|
import { enumPinSlotType } from "../components/wired_pins";
|
||||||
import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON, BooleanItem } from "../items/boolean_item";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
import { enumItemProcessorTypes } from "../components/item_processor";
|
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON, BooleanItem } from "../items/boolean_item";
|
||||||
|
import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
|
||||||
export class LogicGateSystem extends GameSystemWithFilter {
|
import { ShapeDefinition } from "../shape_definition";
|
||||||
constructor(root) {
|
import { ShapeItem } from "../items/shape_item";
|
||||||
super(root, [LogicGateComponent]);
|
|
||||||
|
export class LogicGateSystem extends GameSystemWithFilter {
|
||||||
this.boundOperations = {
|
constructor(root) {
|
||||||
[enumLogicGateType.and]: this.compute_AND.bind(this),
|
super(root, [LogicGateComponent]);
|
||||||
[enumLogicGateType.not]: this.compute_NOT.bind(this),
|
|
||||||
[enumLogicGateType.xor]: this.compute_XOR.bind(this),
|
this.boundOperations = {
|
||||||
[enumLogicGateType.or]: this.compute_OR.bind(this),
|
[enumLogicGateType.and]: this.compute_AND.bind(this),
|
||||||
[enumLogicGateType.transistor]: this.compute_IF.bind(this),
|
[enumLogicGateType.not]: this.compute_NOT.bind(this),
|
||||||
};
|
[enumLogicGateType.xor]: this.compute_XOR.bind(this),
|
||||||
}
|
[enumLogicGateType.or]: this.compute_OR.bind(this),
|
||||||
|
[enumLogicGateType.transistor]: this.compute_IF.bind(this),
|
||||||
update() {
|
|
||||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
[enumLogicGateType.rotater]: this.compute_ROTATE.bind(this),
|
||||||
const entity = this.allEntities[i];
|
[enumLogicGateType.analyzer]: this.compute_ANALYZE.bind(this),
|
||||||
const logicComp = entity.components.LogicGate;
|
[enumLogicGateType.cutter]: this.compute_CUT.bind(this),
|
||||||
const slotComp = entity.components.WiredPins;
|
[enumLogicGateType.unstacker]: this.compute_UNSTACK.bind(this),
|
||||||
|
[enumLogicGateType.shapecompare]: this.compute_SHAPECOMPARE.bind(this),
|
||||||
const slotValues = [];
|
};
|
||||||
|
}
|
||||||
for (let i = 0; i < slotComp.slots.length; ++i) {
|
|
||||||
const slot = slotComp.slots[i];
|
update() {
|
||||||
if (slot.type !== enumPinSlotType.logicalAcceptor) {
|
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||||
continue;
|
const entity = this.allEntities[i];
|
||||||
}
|
const logicComp = entity.components.LogicGate;
|
||||||
if (slot.linkedNetwork) {
|
const slotComp = entity.components.WiredPins;
|
||||||
slotValues.push(slot.linkedNetwork.currentValue);
|
|
||||||
} else {
|
const slotValues = [];
|
||||||
slotValues.push(null);
|
|
||||||
}
|
for (let i = 0; i < slotComp.slots.length; ++i) {
|
||||||
}
|
const slot = slotComp.slots[i];
|
||||||
|
if (slot.type !== enumPinSlotType.logicalAcceptor) {
|
||||||
const result = this.boundOperations[logicComp.type](slotValues);
|
continue;
|
||||||
|
}
|
||||||
// @TODO: For now we hardcode the value to always be slot 0
|
if (slot.linkedNetwork) {
|
||||||
assert(
|
slotValues.push(slot.linkedNetwork.currentValue);
|
||||||
slotValues.length === slotComp.slots.length - 1,
|
} else {
|
||||||
"Bad slot config, should have N acceptor slots and 1 ejector"
|
slotValues.push(null);
|
||||||
);
|
}
|
||||||
assert(slotComp.slots[0].type === enumPinSlotType.logicalEjector, "Slot 0 should be ejector");
|
}
|
||||||
|
|
||||||
slotComp.slots[0].value = result;
|
const result = this.boundOperations[logicComp.type](slotValues);
|
||||||
}
|
|
||||||
}
|
if (Array.isArray(result)) {
|
||||||
|
let resultIndex = 0;
|
||||||
/**
|
for (let i = 0; i < slotComp.slots.length; ++i) {
|
||||||
* @param {Array<BaseItem|null>} parameters
|
const slot = slotComp.slots[i];
|
||||||
* @returns {BaseItem}
|
if (slot.type !== enumPinSlotType.logicalEjector) {
|
||||||
*/
|
continue;
|
||||||
compute_AND(parameters) {
|
}
|
||||||
assert(parameters.length === 2, "bad parameter count for AND");
|
slot.value = result[resultIndex++];
|
||||||
|
}
|
||||||
const param1 = parameters[0];
|
} else {
|
||||||
const param2 = parameters[1];
|
// @TODO: For now we hardcode the value to always be slot 0
|
||||||
if (!param1 || !param2) {
|
assert(
|
||||||
// Not enough params
|
slotValues.length === slotComp.slots.length - 1,
|
||||||
return BOOL_FALSE_SINGLETON;
|
"Bad slot config, should have N acceptor slots and 1 ejector"
|
||||||
}
|
);
|
||||||
|
assert(slotComp.slots[0].type === enumPinSlotType.logicalEjector, "Slot 0 should be ejector");
|
||||||
const itemType = param1.getItemType();
|
slotComp.slots[0].value = result;
|
||||||
|
}
|
||||||
if (itemType !== param2.getItemType()) {
|
}
|
||||||
// Differing type
|
}
|
||||||
return BOOL_FALSE_SINGLETON;
|
|
||||||
}
|
/**
|
||||||
|
* @param {Array<BaseItem|null>} parameters
|
||||||
if (itemType === "boolean") {
|
* @returns {BaseItem}
|
||||||
return /** @type {BooleanItem} */ (param1).value && /** @type {BooleanItem} */ (param2).value
|
*/
|
||||||
? BOOL_TRUE_SINGLETON
|
compute_AND(parameters) {
|
||||||
: BOOL_FALSE_SINGLETON;
|
assert(parameters.length === 2, "bad parameter count for AND");
|
||||||
}
|
|
||||||
|
const param1 = parameters[0];
|
||||||
return BOOL_FALSE_SINGLETON;
|
const param2 = parameters[1];
|
||||||
}
|
if (!param1 || !param2) {
|
||||||
|
// Not enough params
|
||||||
/**
|
return BOOL_FALSE_SINGLETON;
|
||||||
* @param {Array<BaseItem|null>} parameters
|
}
|
||||||
* @returns {BaseItem}
|
|
||||||
*/
|
const itemType = param1.getItemType();
|
||||||
compute_NOT(parameters) {
|
|
||||||
const item = parameters[0];
|
if (itemType !== param2.getItemType()) {
|
||||||
if (!item) {
|
// Differing type
|
||||||
return BOOL_TRUE_SINGLETON;
|
return BOOL_FALSE_SINGLETON;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.getItemType() !== "boolean") {
|
if (itemType === "boolean") {
|
||||||
// Not a boolean actually
|
return /** @type {BooleanItem} */ (param1).value && /** @type {BooleanItem} */ (param2).value
|
||||||
return BOOL_FALSE_SINGLETON;
|
? BOOL_TRUE_SINGLETON
|
||||||
}
|
: BOOL_FALSE_SINGLETON;
|
||||||
|
}
|
||||||
const value = /** @type {BooleanItem} */ (item).value;
|
|
||||||
return value ? BOOL_FALSE_SINGLETON : BOOL_TRUE_SINGLETON;
|
return BOOL_FALSE_SINGLETON;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Array<BaseItem|null>} parameters
|
* @param {Array<BaseItem|null>} parameters
|
||||||
* @returns {BaseItem}
|
* @returns {BaseItem}
|
||||||
*/
|
*/
|
||||||
compute_XOR(parameters) {
|
compute_NOT(parameters) {
|
||||||
assert(parameters.length === 2, "bad parameter count for XOR");
|
const item = parameters[0];
|
||||||
|
if (!item) {
|
||||||
const param1 = parameters[0];
|
return BOOL_TRUE_SINGLETON;
|
||||||
const param2 = parameters[1];
|
}
|
||||||
if (!param1 && !param2) {
|
|
||||||
// Not enough params
|
if (item.getItemType() !== "boolean") {
|
||||||
return BOOL_FALSE_SINGLETON;
|
// Not a boolean actually
|
||||||
}
|
return BOOL_FALSE_SINGLETON;
|
||||||
|
}
|
||||||
// Check for the right types
|
|
||||||
if (param1 && param1.getItemType() !== "boolean") {
|
const value = /** @type {BooleanItem} */ (item).value;
|
||||||
return BOOL_FALSE_SINGLETON;
|
return value ? BOOL_FALSE_SINGLETON : BOOL_TRUE_SINGLETON;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param2 && param2.getItemType() !== "boolean") {
|
/**
|
||||||
return BOOL_FALSE_SINGLETON;
|
* @param {Array<BaseItem|null>} parameters
|
||||||
}
|
* @returns {BaseItem}
|
||||||
|
*/
|
||||||
const valueParam1 = param1 ? /** @type {BooleanItem} */ (param1).value : 0;
|
compute_XOR(parameters) {
|
||||||
const valueParam2 = param2 ? /** @type {BooleanItem} */ (param2).value : 0;
|
assert(parameters.length === 2, "bad parameter count for XOR");
|
||||||
|
|
||||||
return valueParam1 ^ valueParam2 ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
|
const param1 = parameters[0];
|
||||||
}
|
const param2 = parameters[1];
|
||||||
|
if (!param1 && !param2) {
|
||||||
/**
|
// Not enough params
|
||||||
* @param {Array<BaseItem|null>} parameters
|
return BOOL_FALSE_SINGLETON;
|
||||||
* @returns {BaseItem}
|
}
|
||||||
*/
|
|
||||||
compute_OR(parameters) {
|
// Check for the right types
|
||||||
assert(parameters.length === 2, "bad parameter count for OR");
|
if (param1 && param1.getItemType() !== "boolean") {
|
||||||
|
return BOOL_FALSE_SINGLETON;
|
||||||
const param1 = parameters[0];
|
}
|
||||||
const param2 = parameters[1];
|
|
||||||
if (!param1 && !param2) {
|
if (param2 && param2.getItemType() !== "boolean") {
|
||||||
// Not enough params
|
return BOOL_FALSE_SINGLETON;
|
||||||
return BOOL_FALSE_SINGLETON;
|
}
|
||||||
}
|
|
||||||
|
const valueParam1 = param1 ? /** @type {BooleanItem} */ (param1).value : 0;
|
||||||
const valueParam1 =
|
const valueParam2 = param2 ? /** @type {BooleanItem} */ (param2).value : 0;
|
||||||
param1 && param1.getItemType() === "boolean" ? /** @type {BooleanItem} */ (param1).value : 0;
|
|
||||||
const valueParam2 =
|
return valueParam1 ^ valueParam2 ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
|
||||||
param2 && param2.getItemType() === "boolean" ? /** @type {BooleanItem} */ (param2).value : 0;
|
}
|
||||||
|
|
||||||
return valueParam1 || valueParam2 ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
|
/**
|
||||||
}
|
* @param {Array<BaseItem|null>} parameters
|
||||||
|
* @returns {BaseItem}
|
||||||
/**
|
*/
|
||||||
* @param {Array<BaseItem|null>} parameters
|
compute_OR(parameters) {
|
||||||
* @returns {BaseItem}
|
assert(parameters.length === 2, "bad parameter count for OR");
|
||||||
*/
|
|
||||||
compute_IF(parameters) {
|
const param1 = parameters[0];
|
||||||
assert(parameters.length === 2, "bad parameter count for IF");
|
const param2 = parameters[1];
|
||||||
|
if (!param1 && !param2) {
|
||||||
const flag = parameters[0];
|
// Not enough params
|
||||||
const value = parameters[1];
|
return BOOL_FALSE_SINGLETON;
|
||||||
if (!flag || !value) {
|
}
|
||||||
// Not enough params
|
|
||||||
return null;
|
const valueParam1 =
|
||||||
}
|
param1 && param1.getItemType() === "boolean" ? /** @type {BooleanItem} */ (param1).value : 0;
|
||||||
|
const valueParam2 =
|
||||||
if (flag.getItemType() !== "boolean") {
|
param2 && param2.getItemType() === "boolean" ? /** @type {BooleanItem} */ (param2).value : 0;
|
||||||
// Flag is not a boolean
|
|
||||||
return null;
|
return valueParam1 || valueParam2 ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
|
||||||
}
|
}
|
||||||
|
|
||||||
// pass through item
|
/**
|
||||||
if (/** @type {BooleanItem} */ (flag).value) {
|
* @param {Array<BaseItem|null>} parameters
|
||||||
return value;
|
* @returns {BaseItem}
|
||||||
}
|
*/
|
||||||
|
compute_IF(parameters) {
|
||||||
return null;
|
assert(parameters.length === 2, "bad parameter count for IF");
|
||||||
}
|
|
||||||
}
|
const flag = parameters[0];
|
||||||
|
const value = parameters[1];
|
||||||
|
if (!flag || !value) {
|
||||||
|
// Not enough params
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flag.getItemType() !== "boolean") {
|
||||||
|
// Flag is not a boolean
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass through item
|
||||||
|
if (/** @type {BooleanItem} */ (flag).value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array<BaseItem|null>} parameters
|
||||||
|
* @returns {BaseItem}
|
||||||
|
*/
|
||||||
|
compute_ROTATE(parameters) {
|
||||||
|
const item = parameters[0];
|
||||||
|
if (!item || item.getItemType() !== "shape") {
|
||||||
|
// Not a shape
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const definition = /** @type {ShapeItem} */ (item).definition;
|
||||||
|
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(definition);
|
||||||
|
return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array<BaseItem|null>} parameters
|
||||||
|
* @returns {[BaseItem, BaseItem]}
|
||||||
|
*/
|
||||||
|
compute_ANALYZE(parameters) {
|
||||||
|
const item = parameters[0];
|
||||||
|
if (!item || item.getItemType() !== "shape") {
|
||||||
|
// Not a shape
|
||||||
|
return [null, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
const definition = /** @type {ShapeItem} */ (item).definition;
|
||||||
|
const lowerLayer = /** @type {import("../shape_definition").ShapeLayer} */ (definition.layers[0]);
|
||||||
|
if (!lowerLayer) {
|
||||||
|
return [null, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
const topRightContent = lowerLayer[0];
|
||||||
|
|
||||||
|
if (!topRightContent || topRightContent.subShape === null) {
|
||||||
|
return [null, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
const newDefinition = new ShapeDefinition({
|
||||||
|
layers: [
|
||||||
|
[
|
||||||
|
{ subShape: topRightContent.subShape, color: enumColors.uncolored },
|
||||||
|
{ subShape: topRightContent.subShape, color: enumColors.uncolored },
|
||||||
|
{ subShape: topRightContent.subShape, color: enumColors.uncolored },
|
||||||
|
{ subShape: topRightContent.subShape, color: enumColors.uncolored },
|
||||||
|
],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return [
|
||||||
|
COLOR_ITEM_SINGLETONS[topRightContent.color],
|
||||||
|
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(newDefinition),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array<BaseItem|null>} parameters
|
||||||
|
* @returns {[BaseItem, BaseItem]}
|
||||||
|
*/
|
||||||
|
compute_CUT(parameters) {
|
||||||
|
const item = parameters[0];
|
||||||
|
if (!item || item.getItemType() !== "shape") {
|
||||||
|
// Not a shape
|
||||||
|
return [null, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
const definition = /** @type {ShapeItem} */ (item).definition;
|
||||||
|
const result = this.root.shapeDefinitionMgr.shapeActionCutHalf(definition);
|
||||||
|
return [
|
||||||
|
result[0].isEntirelyEmpty()
|
||||||
|
? null
|
||||||
|
: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(result[0]),
|
||||||
|
result[1].isEntirelyEmpty()
|
||||||
|
? null
|
||||||
|
: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(result[1]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array<BaseItem|null>} parameters
|
||||||
|
* @returns {[BaseItem, BaseItem]}
|
||||||
|
*/
|
||||||
|
compute_UNSTACK(parameters) {
|
||||||
|
const item = parameters[0];
|
||||||
|
if (!item || item.getItemType() !== "shape") {
|
||||||
|
// Not a shape
|
||||||
|
return [null, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
const definition = /** @type {ShapeItem} */ (item).definition;
|
||||||
|
const layers = /** @type {Array<import("../shape_definition").ShapeLayer>} */ (definition.layers);
|
||||||
|
|
||||||
|
const upperLayerDefinition = new ShapeDefinition({
|
||||||
|
layers: [layers[layers.length - 1]],
|
||||||
|
});
|
||||||
|
|
||||||
|
const lowerLayers = layers.slice(0, layers.length - 1);
|
||||||
|
const lowerLayerDefinition =
|
||||||
|
lowerLayers.length > 0 ? new ShapeDefinition({ layers: lowerLayers }) : null;
|
||||||
|
|
||||||
|
return [
|
||||||
|
lowerLayerDefinition
|
||||||
|
? this.root.shapeDefinitionMgr.getShapeItemFromDefinition(lowerLayerDefinition)
|
||||||
|
: null,
|
||||||
|
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(upperLayerDefinition),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array<BaseItem|null>} parameters
|
||||||
|
* @returns {BaseItem}
|
||||||
|
*/
|
||||||
|
compute_SHAPECOMPARE(parameters) {
|
||||||
|
const itemA = parameters[0];
|
||||||
|
const itemB = parameters[1];
|
||||||
|
|
||||||
|
return itemA &&
|
||||||
|
itemB &&
|
||||||
|
itemA.getItemType() === "shape" &&
|
||||||
|
itemB.getItemType() === "shape" &&
|
||||||
|
/** @type {ShapeItem} */ (itemA).definition.getHash() ===
|
||||||
|
/** @type {ShapeItem} */ (itemB).definition.getHash()
|
||||||
|
? BOOL_TRUE_SINGLETON
|
||||||
|
: BOOL_FALSE_SINGLETON;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,7 +13,7 @@ export class MapResourcesSystem extends GameSystem {
|
|||||||
*/
|
*/
|
||||||
drawChunk(parameters, chunk) {
|
drawChunk(parameters, chunk) {
|
||||||
const basicChunkBackground = this.root.buffers.getForKey({
|
const basicChunkBackground = this.root.buffers.getForKey({
|
||||||
key: "chunkres",
|
key: "mapresourcebg",
|
||||||
subKey: chunk.renderKey,
|
subKey: chunk.renderKey,
|
||||||
w: globalConfig.mapChunkSize,
|
w: globalConfig.mapChunkSize,
|
||||||
h: globalConfig.mapChunkSize,
|
h: globalConfig.mapChunkSize,
|
||||||
@ -42,10 +42,9 @@ export class MapResourcesSystem extends GameSystem {
|
|||||||
const patch = chunk.patches[i];
|
const patch = chunk.patches[i];
|
||||||
const destX = chunk.x * globalConfig.mapChunkWorldSize + patch.pos.x * globalConfig.tileSize;
|
const destX = chunk.x * globalConfig.mapChunkWorldSize + patch.pos.x * globalConfig.tileSize;
|
||||||
const destY = chunk.y * globalConfig.mapChunkWorldSize + patch.pos.y * globalConfig.tileSize;
|
const destY = chunk.y * globalConfig.mapChunkWorldSize + patch.pos.y * globalConfig.tileSize;
|
||||||
const destSize = Math.min(80, 40 / parameters.zoomLevel);
|
const diameter = Math.min(80, 40 / parameters.zoomLevel);
|
||||||
if (parameters.visibleRect.containsCircle(destX, destY, destSize / 2)) {
|
|
||||||
patch.item.drawCentered(destX, destY, parameters, destSize);
|
patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// HIGH QUALITY: Draw all items
|
// HIGH QUALITY: Draw all items
|
||||||
@ -61,9 +60,12 @@ export class MapResourcesSystem extends GameSystem {
|
|||||||
const destX = worldX + globalConfig.halfTileSize;
|
const destX = worldX + globalConfig.halfTileSize;
|
||||||
const destY = worldY + globalConfig.halfTileSize;
|
const destY = worldY + globalConfig.halfTileSize;
|
||||||
|
|
||||||
if (parameters.visibleRect.containsCircle(destX, destY, globalConfig.tileSize / 2)) {
|
lowerItem.drawItemCenteredClipped(
|
||||||
lowerItem.drawCentered(destX, destY, parameters);
|
destX,
|
||||||
}
|
destY,
|
||||||
|
parameters,
|
||||||
|
globalConfig.defaultItemDiameter
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,41 +102,39 @@ export class MinerSystem extends GameSystemWithFilter {
|
|||||||
* @param {MapChunkView} chunk
|
* @param {MapChunkView} chunk
|
||||||
*/
|
*/
|
||||||
drawChunk(parameters, chunk) {
|
drawChunk(parameters, chunk) {
|
||||||
const contents = chunk.contents;
|
const contents = chunk.containedEntitiesByLayer.regular;
|
||||||
for (let y = 0; y < globalConfig.mapChunkSize; ++y) {
|
|
||||||
for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
|
|
||||||
const entity = contents[x][y];
|
|
||||||
|
|
||||||
if (entity && entity.components.Miner) {
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
const entity = contents[i];
|
||||||
const minerComp = entity.components.Miner;
|
const minerComp = entity.components.Miner;
|
||||||
if (!staticComp.shouldBeDrawn(parameters)) {
|
if (!minerComp) {
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
if (!minerComp.cachedMinedItem) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minerComp.cachedMinedItem) {
|
|
||||||
const padding = 3;
|
|
||||||
parameters.context.fillStyle = minerComp.cachedMinedItem.getBackgroundColorAsResource();
|
|
||||||
parameters.context.fillRect(
|
|
||||||
staticComp.origin.x * globalConfig.tileSize + padding,
|
|
||||||
staticComp.origin.y * globalConfig.tileSize + padding,
|
|
||||||
globalConfig.tileSize - 2 * padding,
|
|
||||||
globalConfig.tileSize - 2 * padding
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minerComp.cachedMinedItem) {
|
|
||||||
minerComp.cachedMinedItem.drawCentered(
|
|
||||||
(0.5 + staticComp.origin.x) * globalConfig.tileSize,
|
|
||||||
(0.5 + staticComp.origin.y) * globalConfig.tileSize,
|
|
||||||
parameters
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
|
if (!minerComp.cachedMinedItem) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the item background - this is to hide the ejected item animation from
|
||||||
|
// the item ejecto
|
||||||
|
|
||||||
|
const padding = 3;
|
||||||
|
const destX = staticComp.origin.x * globalConfig.tileSize + padding;
|
||||||
|
const destY = staticComp.origin.y * globalConfig.tileSize + padding;
|
||||||
|
const dimensions = globalConfig.tileSize - 2 * padding;
|
||||||
|
|
||||||
|
if (parameters.visibleRect.containsRect4Params(destX, destY, dimensions, dimensions)) {
|
||||||
|
parameters.context.fillStyle = minerComp.cachedMinedItem.getBackgroundColorAsResource();
|
||||||
|
parameters.context.fillRect(destX, destY, dimensions, dimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
minerComp.cachedMinedItem.drawItemCenteredClipped(
|
||||||
|
(0.5 + staticComp.origin.x) * globalConfig.tileSize,
|
||||||
|
(0.5 + staticComp.origin.y) * globalConfig.tileSize,
|
||||||
|
parameters,
|
||||||
|
globalConfig.defaultItemDiameter
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,18 @@ import { MapChunkView } from "../map_chunk_view";
|
|||||||
export class StaticMapEntitySystem extends GameSystem {
|
export class StaticMapEntitySystem extends GameSystem {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
super(root);
|
super(root);
|
||||||
|
|
||||||
|
/** @type {Set<number>} */
|
||||||
|
this.drawnUids = new Set();
|
||||||
|
|
||||||
|
this.root.signals.gameFrameStarted.add(this.clearUidList, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the uid list when a new frame started
|
||||||
|
*/
|
||||||
|
clearUidList() {
|
||||||
|
this.drawnUids.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,25 +30,21 @@ export class StaticMapEntitySystem extends GameSystem {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const drawnUids = new Set();
|
const contents = chunk.containedEntitiesByLayer.regular;
|
||||||
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
|
const entity = contents[i];
|
||||||
|
|
||||||
const contents = chunk.contents;
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
for (let y = 0; y < globalConfig.mapChunkSize; ++y) {
|
const sprite = staticComp.getSprite();
|
||||||
for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
|
if (sprite) {
|
||||||
const entity = contents[x][y];
|
// Avoid drawing an entity twice which has been drawn for
|
||||||
|
// another chunk already
|
||||||
if (entity) {
|
if (this.drawnUids.has(entity.uid)) {
|
||||||
if (drawnUids.has(entity.uid)) {
|
continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
drawnUids.add(entity.uid);
|
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
|
||||||
|
|
||||||
const sprite = staticComp.getSprite();
|
|
||||||
if (sprite) {
|
|
||||||
staticComp.drawSpriteOnFullEntityBounds(parameters, sprite, 2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.drawnUids.add(entity.uid);
|
||||||
|
staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,7 +73,7 @@ export class StaticMapEntitySystem extends GameSystem {
|
|||||||
|
|
||||||
const sprite = staticComp.getSprite();
|
const sprite = staticComp.getSprite();
|
||||||
if (sprite) {
|
if (sprite) {
|
||||||
staticComp.drawSpriteOnFullEntityBounds(parameters, sprite, 2);
|
staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,28 @@
|
|||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
import { StorageComponent } from "../components/storage";
|
import { StorageComponent } from "../components/storage";
|
||||||
import { Entity } from "../entity";
|
|
||||||
import { DrawParameters } from "../../core/draw_parameters";
|
import { DrawParameters } from "../../core/draw_parameters";
|
||||||
import { formatBigNumber, lerp } from "../../core/utils";
|
import { formatBigNumber, lerp } from "../../core/utils";
|
||||||
import { Loader } from "../../core/loader";
|
import { Loader } from "../../core/loader";
|
||||||
import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON } from "../items/boolean_item";
|
import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON } from "../items/boolean_item";
|
||||||
|
import { MapChunkView } from "../map_chunk_view";
|
||||||
|
|
||||||
export class StorageSystem extends GameSystemWithFilter {
|
export class StorageSystem extends GameSystemWithFilter {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
super(root, [StorageComponent]);
|
super(root, [StorageComponent]);
|
||||||
|
|
||||||
this.storageOverlaySprite = Loader.getSprite("sprites/misc/storage_overlay.png");
|
this.storageOverlaySprite = Loader.getSprite("sprites/misc/storage_overlay.png");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores which uids were already drawn to avoid drawing entities twice
|
||||||
|
* @type {Set<number>}
|
||||||
|
*/
|
||||||
|
this.drawnUids = new Set();
|
||||||
|
|
||||||
|
this.root.signals.gameFrameStarted.add(this.clearDrawnUids, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearDrawnUids() {
|
||||||
|
this.drawnUids.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
@ -43,38 +55,46 @@ export class StorageSystem extends GameSystemWithFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(parameters) {
|
|
||||||
this.forEachMatchingEntityOnScreen(parameters, this.drawEntity.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
* @param {Entity} entity
|
* @param {MapChunkView} chunk
|
||||||
*/
|
*/
|
||||||
drawEntity(parameters, entity) {
|
drawChunk(parameters, chunk) {
|
||||||
const context = parameters.context;
|
const contents = chunk.containedEntitiesByLayer.regular;
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
|
const entity = contents[i];
|
||||||
|
const storageComp = entity.components.Storage;
|
||||||
|
if (!storageComp) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!staticComp.shouldBeDrawn(parameters)) {
|
const storedItem = storageComp.storedItem;
|
||||||
return;
|
if (!storedItem) {
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const storageComp = entity.components.Storage;
|
if (this.drawnUids.has(entity.uid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const storedItem = storageComp.storedItem;
|
this.drawnUids.add(entity.uid);
|
||||||
if (storedItem !== null) {
|
|
||||||
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
|
|
||||||
|
const context = parameters.context;
|
||||||
context.globalAlpha = storageComp.overlayOpacity;
|
context.globalAlpha = storageComp.overlayOpacity;
|
||||||
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
||||||
storedItem.drawCentered(center.x, center.y, parameters, 30);
|
storedItem.drawItemCenteredClipped(center.x, center.y, parameters, 30);
|
||||||
|
|
||||||
this.storageOverlaySprite.drawCached(parameters, center.x - 15, center.y + 15, 30, 15);
|
this.storageOverlaySprite.drawCached(parameters, center.x - 15, center.y + 15, 30, 15);
|
||||||
|
|
||||||
context.font = "bold 10px GameFont";
|
if (parameters.visibleRect.containsCircle(center.x, center.y + 25, 20)) {
|
||||||
context.textAlign = "center";
|
context.font = "bold 10px GameFont";
|
||||||
context.fillStyle = "#64666e";
|
context.textAlign = "center";
|
||||||
context.fillText(formatBigNumber(storageComp.storedCount), center.x, center.y + 25.5);
|
context.fillStyle = "#64666e";
|
||||||
|
context.fillText(formatBigNumber(storageComp.storedCount), center.x, center.y + 25.5);
|
||||||
context.textAlign = "left";
|
context.textAlign = "left";
|
||||||
|
}
|
||||||
context.globalAlpha = 1;
|
context.globalAlpha = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,7 +317,9 @@ export class WireSystem extends GameSystemWithFilter {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
currentNetwork.providers.length > 0 &&
|
currentNetwork.providers.length > 0 &&
|
||||||
(currentNetwork.wires.length > 0 || currentNetwork.receivers.length > 0)
|
(currentNetwork.wires.length > 0 ||
|
||||||
|
currentNetwork.receivers.length > 0 ||
|
||||||
|
currentNetwork.tunnels.length > 0)
|
||||||
) {
|
) {
|
||||||
this.networks.push(currentNetwork);
|
this.networks.push(currentNetwork);
|
||||||
VERBOSE_WIRES && logger.log("Attached new network with uid", currentNetwork);
|
VERBOSE_WIRES && logger.log("Attached new network with uid", currentNetwork);
|
||||||
@ -624,7 +626,7 @@ export class WireSystem extends GameSystemWithFilter {
|
|||||||
assert(sprite, "Unknown wire type: " + wireType);
|
assert(sprite, "Unknown wire type: " + wireType);
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
parameters.context.globalAlpha = opacity;
|
parameters.context.globalAlpha = opacity;
|
||||||
staticComp.drawSpriteOnFullEntityBounds(parameters, sprite, 0);
|
staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 0);
|
||||||
parameters.context.globalAlpha = 1;
|
parameters.context.globalAlpha = 1;
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.renderWireRotations) {
|
if (G_IS_DEV && globalConfig.debug.renderWireRotations) {
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { globalConfig } from "../../core/config";
|
import { globalConfig } from "../../core/config";
|
||||||
import { DrawParameters } from "../../core/draw_parameters";
|
import { DrawParameters } from "../../core/draw_parameters";
|
||||||
|
import { drawRotatedSprite } from "../../core/draw_utils";
|
||||||
import { Loader } from "../../core/loader";
|
import { Loader } from "../../core/loader";
|
||||||
import { Vector, enumDirectionToAngle } from "../../core/vector";
|
import { STOP_PROPAGATION } from "../../core/signal";
|
||||||
|
import { enumDirectionToAngle, 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 { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
import { STOP_PROPAGATION } from "../../core/signal";
|
import { MapChunkView } from "../map_chunk_view";
|
||||||
import { drawRotatedSprite } from "../../core/draw_utils";
|
|
||||||
import { GLOBAL_APP } from "../../core/globals";
|
|
||||||
|
|
||||||
export class WiredPinsSystem extends GameSystemWithFilter {
|
export class WiredPinsSystem extends GameSystemWithFilter {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
@ -146,65 +146,84 @@ export class WiredPinsSystem extends GameSystemWithFilter {
|
|||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the pins
|
|
||||||
* @param {DrawParameters} parameters
|
|
||||||
*/
|
|
||||||
draw(parameters) {
|
|
||||||
this.forEachMatchingEntityOnScreen(parameters, this.drawSingleEntity.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a given entity
|
* Draws a given entity
|
||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
* @param {Entity} entity
|
* @param {MapChunkView} chunk
|
||||||
*/
|
*/
|
||||||
drawSingleEntity(parameters, entity) {
|
drawChunk(parameters, chunk) {
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
const contents = chunk.containedEntities;
|
||||||
const slots = entity.components.WiredPins.slots;
|
|
||||||
|
|
||||||
for (let i = 0; i < slots.length; ++i) {
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
const slot = slots[i];
|
const entity = contents[i];
|
||||||
const tile = staticComp.localTileToWorld(slot.pos);
|
const pinsComp = entity.components.WiredPins;
|
||||||
|
if (!pinsComp) {
|
||||||
const worldPos = tile.toWorldSpaceCenterOfTile();
|
continue;
|
||||||
const effectiveRotation = Math.radians(
|
|
||||||
staticComp.rotation + enumDirectionToAngle[slot.direction]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (staticComp.getMetaBuilding().getRenderPins()) {
|
|
||||||
drawRotatedSprite({
|
|
||||||
parameters,
|
|
||||||
sprite: this.pinSprites[slot.type],
|
|
||||||
x: worldPos.x,
|
|
||||||
y: worldPos.y,
|
|
||||||
angle: effectiveRotation,
|
|
||||||
size: globalConfig.tileSize + 2,
|
|
||||||
offsetX: 0,
|
|
||||||
offsetY: 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw contained item to visualize whats emitted
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
const value = slot.value;
|
const slots = pinsComp.slots;
|
||||||
if (value) {
|
|
||||||
const offset = new Vector(0, -9).rotated(effectiveRotation);
|
|
||||||
value.drawCentered(worldPos.x + offset.x, worldPos.y + offset.y, parameters, 9);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug view
|
for (let j = 0; j < slots.length; ++j) {
|
||||||
if (G_IS_DEV && globalConfig.debug.renderWireNetworkInfos) {
|
const slot = slots[j];
|
||||||
const offset = new Vector(0, -10).rotated(effectiveRotation);
|
const tile = staticComp.localTileToWorld(slot.pos);
|
||||||
const network = slot.linkedNetwork;
|
|
||||||
parameters.context.fillStyle = "blue";
|
if (!chunk.tileSpaceRectangle.containsPoint(tile.x, tile.y)) {
|
||||||
parameters.context.font = "5px Tahoma";
|
// Doesn't belong to this chunk
|
||||||
parameters.context.textAlign = "center";
|
continue;
|
||||||
parameters.context.fillText(
|
}
|
||||||
network ? "S" + network.uid : "???",
|
const worldPos = tile.toWorldSpaceCenterOfTile();
|
||||||
(tile.x + 0.5) * globalConfig.tileSize + offset.x,
|
|
||||||
(tile.y + 0.5) * globalConfig.tileSize + offset.y
|
// Culling
|
||||||
|
if (
|
||||||
|
!parameters.visibleRect.containsCircle(worldPos.x, worldPos.y, globalConfig.halfTileSize)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const effectiveRotation = Math.radians(
|
||||||
|
staticComp.rotation + enumDirectionToAngle[slot.direction]
|
||||||
);
|
);
|
||||||
parameters.context.textAlign = "left";
|
|
||||||
|
if (staticComp.getMetaBuilding().getRenderPins()) {
|
||||||
|
drawRotatedSprite({
|
||||||
|
parameters,
|
||||||
|
sprite: this.pinSprites[slot.type],
|
||||||
|
x: worldPos.x,
|
||||||
|
y: worldPos.y,
|
||||||
|
angle: effectiveRotation,
|
||||||
|
size: globalConfig.tileSize + 2,
|
||||||
|
offsetX: 0,
|
||||||
|
offsetY: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw contained item to visualize whats emitted
|
||||||
|
const value = slot.value;
|
||||||
|
if (value) {
|
||||||
|
const offset = new Vector(0, -9).rotated(effectiveRotation);
|
||||||
|
value.drawItemCenteredClipped(
|
||||||
|
worldPos.x + offset.x,
|
||||||
|
worldPos.y + offset.y,
|
||||||
|
parameters,
|
||||||
|
9
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug view
|
||||||
|
if (G_IS_DEV && globalConfig.debug.renderWireNetworkInfos) {
|
||||||
|
const offset = new Vector(0, -10).rotated(effectiveRotation);
|
||||||
|
const network = slot.linkedNetwork;
|
||||||
|
parameters.context.fillStyle = "blue";
|
||||||
|
parameters.context.font = "5px Tahoma";
|
||||||
|
parameters.context.textAlign = "center";
|
||||||
|
parameters.context.fillText(
|
||||||
|
network ? "S" + network.uid : "???",
|
||||||
|
(tile.x + 0.5) * globalConfig.tileSize + offset.x,
|
||||||
|
(tile.y + 0.5) * globalConfig.tileSize + offset.y
|
||||||
|
);
|
||||||
|
parameters.context.textAlign = "left";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ steamPage:
|
|||||||
|
|
||||||
Nutze dein gesammeltes Wissen über die Maschinen und lasse deine Fabriken die gewünschten Formen der 18 verschiedenen Level abliefern. Schalte mit jedem Level neue Arbeitsschritte oder Gebäude frei. Das sollte dich schon für Stunden beschäftigt halten! Danach werden im Freispielmodus zufällige Formen generiert, die du ebenfalls abliefern kannst. Ich füge regelmäßig neue Funktionen hinzu und davon sind eine ganze Menge geplant!
|
Nutze dein gesammeltes Wissen über die Maschinen und lasse deine Fabriken die gewünschten Formen der 18 verschiedenen Level abliefern. Schalte mit jedem Level neue Arbeitsschritte oder Gebäude frei. Das sollte dich schon für Stunden beschäftigt halten! Danach werden im Freispielmodus zufällige Formen generiert, die du ebenfalls abliefern kannst. Ich füge regelmäßig neue Funktionen hinzu und davon sind eine ganze Menge geplant!
|
||||||
|
|
||||||
|
|
||||||
Wenn du das Spiel erwirbst, erhälst du Zugriff auf die zusätzlichen Features der Standalone-Version. Das bedeutet, du kannst unter anderem die neuesten Updates zuerst spielen!
|
Wenn du das Spiel erwirbst, erhälst du Zugriff auf die zusätzlichen Features der Standalone-Version. Das bedeutet, du kannst unter anderem die neuesten Updates zuerst spielen!
|
||||||
|
|
||||||
[b]Vorteile der Standalone[/b]
|
[b]Vorteile der Standalone[/b]
|
||||||
@ -296,6 +297,7 @@ ingame:
|
|||||||
copySelection: Kopieren
|
copySelection: Kopieren
|
||||||
clearSelection: Auswahl aufheben
|
clearSelection: Auswahl aufheben
|
||||||
pipette: Pipette
|
pipette: Pipette
|
||||||
|
|
||||||
switchLayers: Ebenen wechseln
|
switchLayers: Ebenen wechseln
|
||||||
|
|
||||||
# Names of the colors, used for the color blind mode
|
# Names of the colors, used for the color blind mode
|
||||||
@ -832,7 +834,6 @@ keybindings:
|
|||||||
Modifikator: stattdessen gegen den UZS rotieren
|
Modifikator: stattdessen gegen den UZS rotieren
|
||||||
cycleBuildingVariants: Variante wählen
|
cycleBuildingVariants: Variante wählen
|
||||||
confirmMassDelete: Massenlöschung bestätigen
|
confirmMassDelete: Massenlöschung bestätigen
|
||||||
pasteLastBlueprint: Letzte Blaupause einfügen
|
|
||||||
cycleBuildings: Gebäude rotieren
|
cycleBuildings: Gebäude rotieren
|
||||||
lockBeltDirection: Bandplaner aktivieren
|
lockBeltDirection: Bandplaner aktivieren
|
||||||
switchDirectionLockSide: >-
|
switchDirectionLockSide: >-
|
||||||
@ -846,8 +847,10 @@ keybindings:
|
|||||||
placementDisableAutoOrientation: Automatische Orientierung deaktivieren
|
placementDisableAutoOrientation: Automatische Orientierung deaktivieren
|
||||||
placeMultiple: Im Platziermodus bleiben
|
placeMultiple: Im Platziermodus bleiben
|
||||||
placeInverse: Automatische Förderbandorientierung invertieren
|
placeInverse: Automatische Förderbandorientierung invertieren
|
||||||
advanced_processor: Farbnivertierer
|
pasteLastBlueprint: Letzte Blaupause einfügen
|
||||||
|
advanced_processor: Farbinvertierer
|
||||||
energy_generator: Energiegenerator
|
energy_generator: Energiegenerator
|
||||||
|
wire: Energiekabel
|
||||||
|
|
||||||
about:
|
about:
|
||||||
title: Über dieses Spiel
|
title: Über dieses Spiel
|
||||||
|