mirror of
https://github.com/tobspr/shapez.io.git
synced 2024-10-27 20:34:29 +00:00
Huge rendering performance improvements and minor other changes, lots of refactorings
This commit is contained in:
parent
d1a5dd8c9e
commit
b2880700e8
@ -27,6 +27,8 @@ export class BufferMaintainer {
|
||||
|
||||
this.iterationIndex = 1;
|
||||
this.lastIteration = 0;
|
||||
|
||||
this.root.signals.gameFrameStarted.add(this.update, this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,6 +53,8 @@ export const globalConfig = {
|
||||
beltSpeedItemsPerSecond: 2,
|
||||
minerSpeedItemsPerSecond: 0, // COMPUTED
|
||||
|
||||
defaultItemDiameter: 20,
|
||||
|
||||
itemSpacingOnBelts: 0.63,
|
||||
|
||||
wiresSpeedItemsPerSecond: 6,
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { globalConfig } from "../core/config";
|
||||
import { DrawParameters } from "../core/draw_parameters";
|
||||
import { BasicSerializableObject } from "../savegame/serialization";
|
||||
|
||||
@ -57,7 +58,22 @@ export class BaseItem extends BasicSerializableObject {
|
||||
* @param {DrawParameters} parameters
|
||||
* @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() {
|
||||
abstract;
|
||||
|
@ -1194,9 +1194,13 @@ export class BeltPath extends BasicSerializableObject {
|
||||
const worldPos = staticComp.localTileToWorld(localPos).toWorldSpaceCenterOfTile();
|
||||
|
||||
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
|
||||
currentItemPos += distanceAndItem[_nextDistance];
|
||||
|
@ -92,7 +92,7 @@ export class Blueprint {
|
||||
parameters.context.globalAlpha = 1;
|
||||
}
|
||||
|
||||
staticComp.drawSpriteOnFullEntityBounds(parameters, staticComp.getBlueprintSprite(), 0, newPos);
|
||||
staticComp.drawSpriteOnBoundsClipped(parameters, staticComp.getBlueprintSprite(), 0, newPos);
|
||||
}
|
||||
parameters.context.globalAlpha = 1;
|
||||
}
|
||||
|
@ -162,8 +162,9 @@ export class StaticMapEntityComponent extends Component {
|
||||
* @returns {Vector}
|
||||
*/
|
||||
localTileToWorld(localTile) {
|
||||
const result = this.applyRotationToVector(localTile);
|
||||
result.addInplace(this.origin);
|
||||
const result = localTile.rotateFastMultipleOf90(this.rotation);
|
||||
result.x += this.origin.x;
|
||||
result.y += this.origin.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -235,7 +236,7 @@ export class StaticMapEntityComponent extends Component {
|
||||
* @param {number=} extrudePixels How many pixels to extrude the sprite
|
||||
* @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) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { enumDirection, Vector } from "../../core/vector";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { Component } from "../component";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { typeItemSingleton } from "../item_resolver";
|
||||
|
||||
/** @enum {string} */
|
||||
export const enumPinSlotType = {
|
||||
@ -27,6 +29,16 @@ export class WiredPinsComponent extends Component {
|
||||
return "WiredPins";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
slots: types.array(
|
||||
types.structured({
|
||||
value: types.nullable(typeItemSingleton),
|
||||
})
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} param0
|
||||
|
@ -329,8 +329,7 @@ export class GameCore {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update buffers as the very first
|
||||
root.buffers.update();
|
||||
this.root.signals.gameFrameStarted.dispatch();
|
||||
|
||||
root.queue.requireRedraw = false;
|
||||
|
||||
@ -390,33 +389,24 @@ export class GameCore {
|
||||
// Map overview
|
||||
root.map.drawOverlay(params);
|
||||
} else {
|
||||
// Background (grid, resources, etc)
|
||||
root.map.drawBackground(params);
|
||||
|
||||
// Belt items
|
||||
systems.belt.drawBeltItems(params);
|
||||
|
||||
// Items being ejected / accepted currently (animations)
|
||||
systems.itemEjector.draw(params);
|
||||
systems.itemAcceptor.draw(params);
|
||||
|
||||
// Miner & Static map entities
|
||||
// Miner & Static map entities etc.
|
||||
root.map.drawForeground(params);
|
||||
|
||||
// HUB Overlay
|
||||
systems.hub.draw(params);
|
||||
|
||||
// Storage items
|
||||
systems.storage.draw(params);
|
||||
|
||||
// Green wires overlay
|
||||
root.hud.parts.wiresOverlay.draw(params);
|
||||
|
||||
if (this.root.currentLayer === "wires") {
|
||||
// Static map entities
|
||||
root.map.drawWiresForegroundLayer(params);
|
||||
|
||||
// pins
|
||||
systems.wiredPins.draw(params);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,7 @@ import { Entity } from "./entity";
|
||||
import { GameRoot } from "./root";
|
||||
import { GameSystem } from "./game_system";
|
||||
import { arrayDelete, arrayDeleteValue } from "../core/utils";
|
||||
import { DrawParameters } from "../core/draw_parameters";
|
||||
import { globalConfig } from "../core/config";
|
||||
|
||||
export class GameSystemWithFilter extends GameSystem {
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
@ -374,7 +374,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
|
||||
// HACK to draw the entity sprite
|
||||
const previewSprite = metaBuilding.getBlueprintSprite(rotationVariant, this.currentVariant.get());
|
||||
staticComp.origin = worldPos.divideScalar(globalConfig.tileSize).subScalars(0.5, 0.5);
|
||||
staticComp.drawSpriteOnFullEntityBounds(parameters, previewSprite);
|
||||
staticComp.drawSpriteOnBoundsClipped(parameters, previewSprite);
|
||||
staticComp.origin = mouseTile;
|
||||
|
||||
// Draw ejectors
|
||||
|
@ -64,11 +64,16 @@ export class HUDWireInfo extends BaseHUDPart {
|
||||
const network = networks[0];
|
||||
|
||||
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) {
|
||||
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 {
|
||||
network.currentValue.drawCentered(mousePos.x + 20, mousePos.y, parameters, 40);
|
||||
network.currentValue.drawItemCenteredClipped(
|
||||
mousePos.x + 40,
|
||||
mousePos.y + 10,
|
||||
parameters,
|
||||
60
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { Loader } from "../../core/loader";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { globalConfig } from "../../core/config";
|
||||
|
||||
export class BooleanItem extends BaseItem {
|
||||
static getId() {
|
||||
@ -46,7 +47,7 @@ export class BooleanItem extends BaseItem {
|
||||
* @param {number} diameter
|
||||
* @param {DrawParameters} parameters
|
||||
*/
|
||||
drawCentered(x, y, parameters, diameter = 12) {
|
||||
drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
|
||||
let sprite;
|
||||
if (this.value) {
|
||||
sprite = Loader.getSprite("sprites/wires/boolean_true.png");
|
||||
|
@ -5,6 +5,7 @@ import { types } from "../../savegame/serialization";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { enumColors, enumColorsToHexCode } from "../colors";
|
||||
import { THEME } from "../theme";
|
||||
import { drawSpriteClipped } from "../../core/draw_utils";
|
||||
|
||||
export class ColorItem extends BaseItem {
|
||||
static getId() {
|
||||
@ -54,23 +55,33 @@ export class ColorItem extends BaseItem {
|
||||
* @param {number} diameter
|
||||
* @param {DrawParameters} parameters
|
||||
*/
|
||||
drawCentered(x, y, parameters, diameter = 12) {
|
||||
drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
|
||||
if (!this.bufferGenerator) {
|
||||
this.bufferGenerator = this.internalGenerateColorBuffer.bind(this);
|
||||
}
|
||||
|
||||
const realDiameter = diameter * 0.6;
|
||||
const dpi = smoothenDpi(globalConfig.shapesSharpness * parameters.zoomLevel);
|
||||
|
||||
const key = diameter + "/" + dpi;
|
||||
const key = realDiameter + "/" + dpi;
|
||||
const canvas = parameters.root.buffers.getForKey({
|
||||
key,
|
||||
subKey: this.color,
|
||||
w: diameter,
|
||||
h: diameter,
|
||||
w: realDiameter,
|
||||
h: realDiameter,
|
||||
dpi,
|
||||
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 { ShapeDefinition } from "../shape_definition";
|
||||
import { THEME } from "../theme";
|
||||
import { globalConfig } from "../../core/config";
|
||||
|
||||
export class ShapeItem extends BaseItem {
|
||||
static getId() {
|
||||
@ -55,7 +56,7 @@ export class ShapeItem extends BaseItem {
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {number=} diameter
|
||||
*/
|
||||
drawCentered(x, y, parameters, diameter) {
|
||||
drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
|
||||
this.definition.drawCentered(x, y, parameters, diameter);
|
||||
}
|
||||
}
|
||||
|
@ -52,10 +52,16 @@ export class MapChunkView extends MapChunk {
|
||||
*/
|
||||
drawForegroundLayer(parameters) {
|
||||
const systems = this.root.systemMgr.systems;
|
||||
|
||||
systems.itemEjector.drawChunk(parameters, this);
|
||||
systems.itemAcceptor.drawChunk(parameters, this);
|
||||
|
||||
systems.miner.drawChunk(parameters, this);
|
||||
|
||||
systems.staticMapEntities.drawChunk(parameters, this);
|
||||
systems.lever.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 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.drawCentered(destX, destY, parameters, destSize);
|
||||
}
|
||||
patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -265,5 +269,6 @@ export class MapChunkView extends MapChunk {
|
||||
const systems = this.root.systemMgr.systems;
|
||||
systems.wire.drawChunk(parameters, this);
|
||||
systems.staticMapEntities.drawWiresChunk(parameters, this);
|
||||
systems.wiredPins.drawChunk(parameters, this);
|
||||
}
|
||||
}
|
||||
|
@ -149,6 +149,8 @@ export class GameRoot {
|
||||
gameSaved: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got saved
|
||||
gameRestored: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got restored
|
||||
|
||||
gameFrameStarted: /** @type {TypedSignal<[]>} */ (new Signal()), // New frame
|
||||
|
||||
storyGoalCompleted: /** @type {TypedSignal<[number, string]>} */ (new Signal()),
|
||||
upgradePurchased: /** @type {TypedSignal<[string]>} */ (new Signal()),
|
||||
|
||||
|
@ -2,7 +2,6 @@ import { makeOffscreenBuffer } from "../core/buffer_utils";
|
||||
import { globalConfig } from "../core/config";
|
||||
import { smoothenDpi } from "../core/dpi_manager";
|
||||
import { DrawParameters } from "../core/draw_parameters";
|
||||
import { createLogger } from "../core/logging";
|
||||
import { Vector } from "../core/vector";
|
||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
import { enumColors, enumColorsToHexCode, enumColorToShortcode, enumShortcodeToColor } from "./colors";
|
||||
|
@ -501,7 +501,9 @@ export class BeltSystem extends GameSystemWithFilter {
|
||||
if (entity.components.Belt) {
|
||||
const direction = entity.components.Belt.direction;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,10 +39,26 @@ export class BeltUnderlaysSystem extends GameSystemWithFilter {
|
||||
const { pos, direction } = underlays[i];
|
||||
const transformedPos = staticComp.localTileToWorld(pos);
|
||||
|
||||
// Culling
|
||||
if (!chunk.tileSpaceRectangle.containsPoint(transformedPos.x, transformedPos.y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const destX = transformedPos.x * globalConfig.tileSize;
|
||||
const destY = transformedPos.y * globalConfig.tileSize;
|
||||
|
||||
// Culling, #2
|
||||
if (
|
||||
parameters.visibleRect.containsRect4Params(
|
||||
destX,
|
||||
destY,
|
||||
globalConfig.tileSize,
|
||||
globalConfig.tileSize
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const angle = enumDirectionToAngle[staticComp.localDirectionToWorld(direction)];
|
||||
|
||||
// SYNC with systems/belt.js:drawSingleEntity!
|
||||
@ -54,8 +70,8 @@ export class BeltUnderlaysSystem extends GameSystemWithFilter {
|
||||
drawRotatedSprite({
|
||||
parameters,
|
||||
sprite: this.underlayBeltSprites[animationIndex % this.underlayBeltSprites.length],
|
||||
x: (transformedPos.x + 0.5) * globalConfig.tileSize,
|
||||
y: (transformedPos.y + 0.5) * globalConfig.tileSize,
|
||||
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) {
|
||||
const pinsComp = entity.components.WiredPins;
|
||||
const network = pinsComp.slots[0].linkedNetwork;
|
||||
|
||||
if (!network || !network.currentValue) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value = this.getDisplayItem(network.currentValue);
|
||||
|
||||
if (!value) {
|
||||
@ -84,7 +86,7 @@ export class DisplaySystem extends GameSystemWithFilter {
|
||||
globalConfig.tileSize
|
||||
);
|
||||
} else if (value.getItemType() === "shape") {
|
||||
value.drawCentered(
|
||||
value.drawItemCenteredClipped(
|
||||
(origin.x + 0.5) * globalConfig.tileSize,
|
||||
(origin.y + 0.5) * globalConfig.tileSize,
|
||||
parameters,
|
||||
|
@ -5,6 +5,14 @@ import { T } from "../../translations";
|
||||
import { HubComponent } from "../components/hub";
|
||||
import { Entity } from "../entity";
|
||||
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 {
|
||||
constructor(root) {
|
||||
@ -13,8 +21,13 @@ export class HubSystem extends GameSystemWithFilter {
|
||||
this.hubSprite = Loader.getSprite("sprites/buildings/hub.png");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DrawParameters} 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() {
|
||||
@ -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) {
|
||||
const context = parameters.context;
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
redrawHubBaseTexture(canvas, context, w, h, dpi) {
|
||||
// This method is quite ugly, please ignore it!
|
||||
|
||||
if (!staticComp.shouldBeDrawn(parameters)) {
|
||||
return;
|
||||
}
|
||||
context.scale(dpi, dpi);
|
||||
|
||||
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
|
||||
staticComp.drawSpriteOnFullEntityBounds(parameters, this.hubSprite, 2.2);
|
||||
context.clearRect(0, 0, w, h);
|
||||
|
||||
this.hubSprite.draw(context, 0, 0, w, h);
|
||||
|
||||
const definition = this.root.hubGoals.currentGoal.definition;
|
||||
|
||||
definition.drawCentered(pos.x - 25, pos.y - 10, parameters, 40);
|
||||
definition.drawCentered(45, 58, parameters, 36);
|
||||
|
||||
const goals = this.root.hubGoals.currentGoal;
|
||||
|
||||
const textOffsetX = 2;
|
||||
const textOffsetY = -6;
|
||||
const textOffsetX = 70;
|
||||
const textOffsetY = 61;
|
||||
|
||||
// Deliver count
|
||||
const delivered = this.root.hubGoals.getCurrentGoalDelivered();
|
||||
const deliveredText = "" + formatBigNumber(delivered);
|
||||
|
||||
if (delivered > 9999) {
|
||||
context.font = "bold 16px GameFont";
|
||||
@ -66,52 +86,87 @@ export class HubSystem extends GameSystemWithFilter {
|
||||
}
|
||||
context.fillStyle = "#64666e";
|
||||
context.textAlign = "left";
|
||||
context.fillText("" + formatBigNumber(delivered), pos.x + textOffsetX, pos.y + textOffsetY);
|
||||
context.fillText(deliveredText, textOffsetX, textOffsetY);
|
||||
|
||||
// Required
|
||||
context.font = "13px GameFont";
|
||||
context.fillStyle = "#a4a6b0";
|
||||
context.fillText(
|
||||
"/ " + formatBigNumber(goals.required),
|
||||
pos.x + textOffsetX,
|
||||
pos.y + textOffsetY + 13
|
||||
);
|
||||
context.fillText("/ " + formatBigNumber(goals.required), textOffsetX, textOffsetY + 13);
|
||||
|
||||
// Reward
|
||||
const rewardText = T.storyRewards[goals.reward].title.toUpperCase();
|
||||
if (rewardText.length > 12) {
|
||||
context.font = "bold 9px GameFont";
|
||||
context.font = "bold 8px GameFont";
|
||||
} else {
|
||||
context.font = "bold 11px GameFont";
|
||||
context.font = "bold 10px GameFont";
|
||||
}
|
||||
context.fillStyle = "#fd0752";
|
||||
context.textAlign = "center";
|
||||
|
||||
context.fillText(rewardText, pos.x, pos.y + 46);
|
||||
context.fillText(rewardText, HUB_SIZE_PIXELS / 2, 105);
|
||||
|
||||
// Level
|
||||
context.font = "bold 11px GameFont";
|
||||
// Level "8"
|
||||
context.font = "bold 10px GameFont";
|
||||
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.fillStyle = "#fff";
|
||||
context.font = "bold 7px GameFont";
|
||||
context.fillText(T.buildings.hub.levelShortcut, pos.x - 42, pos.y - 47);
|
||||
context.font = "bold 6px GameFont";
|
||||
context.fillText(T.buildings.hub.levelShortcut, 27, 22);
|
||||
|
||||
// "Deliver"
|
||||
context.fillStyle = "#64666e";
|
||||
context.font = "bold 11px GameFont";
|
||||
context.fillText(T.buildings.hub.deliver.toUpperCase(), pos.x, pos.y - 40);
|
||||
context.font = "bold 10px GameFont";
|
||||
context.fillText(T.buildings.hub.deliver.toUpperCase(), HUB_SIZE_PIXELS / 2, 30);
|
||||
|
||||
// "To unlock"
|
||||
const unlockText = T.buildings.hub.toUnlock.toUpperCase();
|
||||
if (unlockText.length > 15) {
|
||||
context.font = "bold 8px GameFont";
|
||||
} 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";
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 { enumDirectionToVector } from "../../core/vector";
|
||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||
import { Entity } from "../entity";
|
||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||
import { MapChunkView } from "../map_chunk_view";
|
||||
|
||||
export class ItemAcceptorSystem extends GameSystemWithFilter {
|
||||
constructor(root) {
|
||||
@ -38,43 +38,45 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the acceptor items
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {MapChunkView} chunk
|
||||
*/
|
||||
draw(parameters) {
|
||||
this.forEachMatchingEntityOnScreen(parameters, this.drawEntityRegularLayer.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
drawEntityRegularLayer(parameters, entity) {
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
drawChunk(parameters, chunk) {
|
||||
const contents = chunk.containedEntitiesByLayer.regular;
|
||||
for (let i = 0; i < contents.length; ++i) {
|
||||
const entity = contents[i];
|
||||
const acceptorComp = entity.components.ItemAcceptor;
|
||||
|
||||
if (!staticComp.shouldBeDrawn(parameters)) {
|
||||
return;
|
||||
if (!acceptorComp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
for (let animIndex = 0; animIndex < acceptorComp.itemConsumptionAnimations.length; ++animIndex) {
|
||||
const { item, slotIndex, animProgress, direction } = acceptorComp.itemConsumptionAnimations[
|
||||
animIndex
|
||||
];
|
||||
|
||||
const slotData = acceptorComp.slots[slotIndex];
|
||||
const realSlotPos = staticComp.localTileToWorld(slotData.pos);
|
||||
|
||||
if (!chunk.tileSpaceRectangle.containsPoint(realSlotPos.x, realSlotPos.y)) {
|
||||
// Not within this chunk
|
||||
continue;
|
||||
}
|
||||
|
||||
const slotWorldPos = staticComp.applyRotationToVector(slotData.pos).add(staticComp.origin);
|
||||
const fadeOutDirection = enumDirectionToVector[staticComp.localDirectionToWorld(direction)];
|
||||
const finalTile = slotWorldPos.subScalars(
|
||||
const finalTile = realSlotPos.subScalars(
|
||||
fadeOutDirection.x * (animProgress / 2 - 0.5),
|
||||
fadeOutDirection.y * (animProgress / 2 - 0.5)
|
||||
);
|
||||
item.drawCentered(
|
||||
|
||||
item.drawItemCenteredClipped(
|
||||
(finalTile.x + 0.5) * globalConfig.tileSize,
|
||||
(finalTile.y + 0.5) * globalConfig.tileSize,
|
||||
parameters
|
||||
parameters,
|
||||
globalConfig.defaultItemDiameter
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import { ItemEjectorComponent } from "../components/item_ejector";
|
||||
import { Entity } from "../entity";
|
||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||
import { enumItemProcessorTypes } from "../components/item_processor";
|
||||
import { MapChunkView } from "../map_chunk_view";
|
||||
|
||||
const logger = createLogger("systems/ejector");
|
||||
|
||||
@ -336,25 +337,21 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws everything
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {MapChunkView} chunk
|
||||
*/
|
||||
draw(parameters) {
|
||||
this.forEachMatchingEntityOnScreen(parameters, this.drawSingleEntity.bind(this));
|
||||
}
|
||||
drawChunk(parameters, chunk) {
|
||||
const contents = chunk.containedEntitiesByLayer.regular;
|
||||
|
||||
/**
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
drawSingleEntity(parameters, entity) {
|
||||
for (let i = 0; i < contents.length; ++i) {
|
||||
const entity = contents[i];
|
||||
const ejectorComp = entity.components.ItemEjector;
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
|
||||
if (!staticComp.shouldBeDrawn(parameters)) {
|
||||
return;
|
||||
if (!ejectorComp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
|
||||
for (let i = 0; i < ejectorComp.slots.length; ++i) {
|
||||
const slot = ejectorComp.slots[i];
|
||||
const ejectedItem = slot.item;
|
||||
@ -364,22 +361,28 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||
continue;
|
||||
}
|
||||
|
||||
const realPosition = slot.pos.rotateFastMultipleOf90(staticComp.rotation);
|
||||
const realDirection = Vector.transformDirectionFromMultipleOf90(
|
||||
slot.direction,
|
||||
staticComp.rotation
|
||||
);
|
||||
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 =
|
||||
staticComp.origin.x + realPosition.x + 0.5 + realDirectionVector.x * 0.5 * slot.progress;
|
||||
const tileY =
|
||||
staticComp.origin.y + realPosition.y + 0.5 + realDirectionVector.y * 0.5 * slot.progress;
|
||||
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.drawCentered(worldX, worldY, parameters);
|
||||
ejectedItem.drawItemCenteredClipped(
|
||||
worldX,
|
||||
worldY,
|
||||
parameters,
|
||||
globalConfig.defaultItemDiameter
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,8 +34,9 @@ export class LeverSystem extends GameSystemWithFilter {
|
||||
const contents = chunk.containedEntitiesByLayer.regular;
|
||||
for (let i = 0; i < contents.length; ++i) {
|
||||
const entity = contents[i];
|
||||
if (entity && entity.components.Lever) {
|
||||
const sprite = entity.components.Lever.toggled ? this.spriteOn : this.spriteOff;
|
||||
const leverComp = entity.components.Lever;
|
||||
if (leverComp) {
|
||||
const sprite = leverComp.toggled ? this.spriteOn : this.spriteOff;
|
||||
const origin = entity.components.StaticMapEntity.origin;
|
||||
sprite.drawCached(
|
||||
parameters,
|
||||
|
@ -42,10 +42,9 @@ export class MapResourcesSystem extends GameSystem {
|
||||
const patch = chunk.patches[i];
|
||||
const destX = chunk.x * globalConfig.mapChunkWorldSize + patch.pos.x * globalConfig.tileSize;
|
||||
const destY = chunk.y * globalConfig.mapChunkWorldSize + patch.pos.y * globalConfig.tileSize;
|
||||
const destSize = Math.min(80, 40 / parameters.zoomLevel);
|
||||
if (parameters.visibleRect.containsCircle(destX, destY, destSize / 2)) {
|
||||
patch.item.drawCentered(destX, destY, parameters, destSize);
|
||||
}
|
||||
const diameter = Math.min(80, 40 / parameters.zoomLevel);
|
||||
|
||||
patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter);
|
||||
}
|
||||
} else {
|
||||
// HIGH QUALITY: Draw all items
|
||||
@ -61,9 +60,12 @@ export class MapResourcesSystem extends GameSystem {
|
||||
const destX = worldX + globalConfig.halfTileSize;
|
||||
const destY = worldY + globalConfig.halfTileSize;
|
||||
|
||||
if (parameters.visibleRect.containsCircle(destX, destY, globalConfig.tileSize / 2)) {
|
||||
lowerItem.drawCentered(destX, destY, parameters);
|
||||
}
|
||||
lowerItem.drawItemCenteredClipped(
|
||||
destX,
|
||||
destY,
|
||||
parameters,
|
||||
globalConfig.defaultItemDiameter
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,41 +102,39 @@ export class MinerSystem extends GameSystemWithFilter {
|
||||
* @param {MapChunkView} chunk
|
||||
*/
|
||||
drawChunk(parameters, chunk) {
|
||||
const contents = chunk.contents;
|
||||
for (let y = 0; y < globalConfig.mapChunkSize; ++y) {
|
||||
for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
|
||||
const entity = contents[x][y];
|
||||
const contents = chunk.containedEntitiesByLayer.regular;
|
||||
|
||||
if (entity && entity.components.Miner) {
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
for (let i = 0; i < contents.length; ++i) {
|
||||
const entity = contents[i];
|
||||
const minerComp = entity.components.Miner;
|
||||
if (!staticComp.shouldBeDrawn(parameters)) {
|
||||
if (!minerComp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
if (!minerComp.cachedMinedItem) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (minerComp.cachedMinedItem) {
|
||||
// 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(
|
||||
staticComp.origin.x * globalConfig.tileSize + padding,
|
||||
staticComp.origin.y * globalConfig.tileSize + padding,
|
||||
globalConfig.tileSize - 2 * padding,
|
||||
globalConfig.tileSize - 2 * padding
|
||||
);
|
||||
parameters.context.fillRect(destX, destY, dimensions, dimensions);
|
||||
}
|
||||
|
||||
if (minerComp.cachedMinedItem) {
|
||||
minerComp.cachedMinedItem.drawCentered(
|
||||
minerComp.cachedMinedItem.drawItemCenteredClipped(
|
||||
(0.5 + staticComp.origin.x) * globalConfig.tileSize,
|
||||
(0.5 + staticComp.origin.y) * globalConfig.tileSize,
|
||||
parameters
|
||||
parameters,
|
||||
globalConfig.defaultItemDiameter
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,18 @@ import { MapChunkView } from "../map_chunk_view";
|
||||
export class StaticMapEntitySystem extends GameSystem {
|
||||
constructor(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;
|
||||
}
|
||||
|
||||
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;
|
||||
for (let y = 0; y < globalConfig.mapChunkSize; ++y) {
|
||||
for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
|
||||
const entity = contents[x][y];
|
||||
|
||||
if (entity) {
|
||||
if (drawnUids.has(entity.uid)) {
|
||||
continue;
|
||||
}
|
||||
drawnUids.add(entity.uid);
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
|
||||
const sprite = staticComp.getSprite();
|
||||
if (sprite) {
|
||||
staticComp.drawSpriteOnFullEntityBounds(parameters, sprite, 2);
|
||||
}
|
||||
// Avoid drawing an entity twice which has been drawn for
|
||||
// another chunk already
|
||||
if (this.drawnUids.has(entity.uid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.drawnUids.add(entity.uid);
|
||||
staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,7 +73,7 @@ export class StaticMapEntitySystem extends GameSystem {
|
||||
|
||||
const sprite = staticComp.getSprite();
|
||||
if (sprite) {
|
||||
staticComp.drawSpriteOnFullEntityBounds(parameters, sprite, 2);
|
||||
staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,28 @@
|
||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||
import { StorageComponent } from "../components/storage";
|
||||
import { Entity } from "../entity";
|
||||
import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { formatBigNumber, lerp } from "../../core/utils";
|
||||
import { Loader } from "../../core/loader";
|
||||
import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON } from "../items/boolean_item";
|
||||
import { MapChunkView } from "../map_chunk_view";
|
||||
|
||||
export class StorageSystem extends GameSystemWithFilter {
|
||||
constructor(root) {
|
||||
super(root, [StorageComponent]);
|
||||
|
||||
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() {
|
||||
@ -43,38 +55,46 @@ export class StorageSystem extends GameSystemWithFilter {
|
||||
}
|
||||
}
|
||||
|
||||
draw(parameters) {
|
||||
this.forEachMatchingEntityOnScreen(parameters, this.drawEntity.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {Entity} entity
|
||||
* @param {MapChunkView} chunk
|
||||
*/
|
||||
drawEntity(parameters, entity) {
|
||||
const context = parameters.context;
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
|
||||
if (!staticComp.shouldBeDrawn(parameters)) {
|
||||
return;
|
||||
drawChunk(parameters, chunk) {
|
||||
const contents = chunk.containedEntitiesByLayer.regular;
|
||||
for (let i = 0; i < contents.length; ++i) {
|
||||
const entity = contents[i];
|
||||
const storageComp = entity.components.Storage;
|
||||
if (!storageComp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const storageComp = entity.components.Storage;
|
||||
|
||||
const storedItem = storageComp.storedItem;
|
||||
if (storedItem !== null) {
|
||||
if (!storedItem) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.drawnUids.has(entity.uid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.drawnUids.add(entity.uid);
|
||||
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
|
||||
const context = parameters.context;
|
||||
context.globalAlpha = storageComp.overlayOpacity;
|
||||
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);
|
||||
|
||||
if (parameters.visibleRect.containsCircle(center.x, center.y + 25, 20)) {
|
||||
context.font = "bold 10px GameFont";
|
||||
context.textAlign = "center";
|
||||
context.fillStyle = "#64666e";
|
||||
context.fillText(formatBigNumber(storageComp.storedCount), center.x, center.y + 25.5);
|
||||
|
||||
context.textAlign = "left";
|
||||
}
|
||||
context.globalAlpha = 1;
|
||||
}
|
||||
}
|
||||
|
@ -624,7 +624,7 @@ export class WireSystem extends GameSystemWithFilter {
|
||||
assert(sprite, "Unknown wire type: " + wireType);
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
parameters.context.globalAlpha = opacity;
|
||||
staticComp.drawSpriteOnFullEntityBounds(parameters, sprite, 0);
|
||||
staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 0);
|
||||
parameters.context.globalAlpha = 1;
|
||||
|
||||
if (G_IS_DEV && globalConfig.debug.renderWireRotations) {
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { globalConfig } from "../../core/config";
|
||||
import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { drawRotatedSprite } from "../../core/draw_utils";
|
||||
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 { Entity } from "../entity";
|
||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||
import { STOP_PROPAGATION } from "../../core/signal";
|
||||
import { drawRotatedSprite } from "../../core/draw_utils";
|
||||
import { GLOBAL_APP } from "../../core/globals";
|
||||
import { MapChunkView } from "../map_chunk_view";
|
||||
|
||||
export class WiredPinsSystem extends GameSystemWithFilter {
|
||||
constructor(root) {
|
||||
@ -146,28 +146,41 @@ export class WiredPinsSystem extends GameSystemWithFilter {
|
||||
// TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the pins
|
||||
* @param {DrawParameters} parameters
|
||||
*/
|
||||
draw(parameters) {
|
||||
this.forEachMatchingEntityOnScreen(parameters, this.drawSingleEntity.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a given entity
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {Entity} entity
|
||||
* @param {MapChunkView} chunk
|
||||
*/
|
||||
drawSingleEntity(parameters, entity) {
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
const slots = entity.components.WiredPins.slots;
|
||||
drawChunk(parameters, chunk) {
|
||||
const contents = chunk.containedEntities;
|
||||
|
||||
for (let i = 0; i < slots.length; ++i) {
|
||||
const slot = slots[i];
|
||||
for (let i = 0; i < contents.length; ++i) {
|
||||
const entity = contents[i];
|
||||
const pinsComp = entity.components.WiredPins;
|
||||
if (!pinsComp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
const slots = pinsComp.slots;
|
||||
|
||||
for (let j = 0; j < slots.length; ++j) {
|
||||
const slot = slots[j];
|
||||
const tile = staticComp.localTileToWorld(slot.pos);
|
||||
|
||||
if (!chunk.tileSpaceRectangle.containsPoint(tile.x, tile.y)) {
|
||||
// Doesn't belong to this chunk
|
||||
continue;
|
||||
}
|
||||
const worldPos = tile.toWorldSpaceCenterOfTile();
|
||||
|
||||
// Culling
|
||||
if (
|
||||
!parameters.visibleRect.containsCircle(worldPos.x, worldPos.y, globalConfig.halfTileSize)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const effectiveRotation = Math.radians(
|
||||
staticComp.rotation + enumDirectionToAngle[slot.direction]
|
||||
);
|
||||
@ -189,7 +202,12 @@ export class WiredPinsSystem extends GameSystemWithFilter {
|
||||
const value = slot.value;
|
||||
if (value) {
|
||||
const offset = new Vector(0, -9).rotated(effectiveRotation);
|
||||
value.drawCentered(worldPos.x + offset.x, worldPos.y + offset.y, parameters, 9);
|
||||
value.drawItemCenteredClipped(
|
||||
worldPos.x + offset.x,
|
||||
worldPos.y + offset.y,
|
||||
parameters,
|
||||
9
|
||||
);
|
||||
}
|
||||
|
||||
// Debug view
|
||||
@ -208,4 +226,5 @@ export class WiredPinsSystem extends GameSystemWithFilter {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user