1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2026-03-02 03:39:21 +00:00

initial take on wire auto-rotation

This commit is contained in:
tobspr
2020-08-11 20:02:59 +02:00
parent a32c0530bb
commit f16ab2389a
18 changed files with 1022 additions and 811 deletions

View File

@@ -683,3 +683,33 @@ export function generateMatrixRotations(originalMatrix) {
return result;
}
/**
*
* @typedef {{
* top: any,
* right: any,
* bottom: any,
* left: any
* }} DirectionalObject
*/
/**
* Rotates a directional object
* @param {DirectionalObject} obj
* @returns {DirectionalObject}
*/
export function rotateDirectionalObject(obj, rotation) {
const queue = [obj.top, obj.right, obj.bottom, obj.left];
while (rotation !== 0) {
rotation -= 90;
queue.push(queue.shift());
}
return {
top: queue[0],
right: queue[1],
bottom: queue[2],
left: queue[3],
};
}

View File

@@ -1,10 +1,11 @@
import { Loader } from "../../core/loader";
import { rotateDirectionalObject } from "../../core/utils";
import { Vector } from "../../core/vector";
import { SOUNDS } from "../../platform/sound";
import { enumWireType, WireComponent } from "../components/wire";
import { Entity } from "../entity";
import { MetaBuilding } from "../meta_building";
import { enumLayer, GameRoot } from "../root";
import { beltOverlayMatrices } from "./belt_base";
export const arrayWireRotationVariantToType = [enumWireType.regular, enumWireType.turn, enumWireType.split];
@@ -108,4 +109,157 @@ export class MetaWireBuilding extends MetaBuilding {
getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) {
return wireOverlayMatrices[entity.components.Wire.type][rotation];
}
getPreviewSprite(rotationVariant) {
switch (arrayWireRotationVariantToType[rotationVariant]) {
case enumWireType.regular: {
return Loader.getSprite("sprites/buildings/wire.png");
}
case enumWireType.turn: {
return Loader.getSprite("sprites/buildings/wire-turn.png");
}
case enumWireType.split: {
return Loader.getSprite("sprites/buildings/wire-split.png");
}
default: {
assertAlways(false, "Invalid belt rotation variant");
}
}
}
getBlueprintSprite(rotationVariant) {
switch (arrayWireRotationVariantToType[rotationVariant]) {
case enumWireType.regular: {
return Loader.getSprite("sprites/blueprints/wire.png");
}
case enumWireType.turn: {
return Loader.getSprite("sprites/blueprints/wire-turn.png");
}
case enumWireType.split: {
return Loader.getSprite("sprites/blueprints/wire-split.png");
}
default: {
assertAlways(false, "Invalid belt rotation variant");
}
}
}
/**
* Should compute the optimal rotation variant on the given tile
* @param {object} param0
* @param {GameRoot} param0.root
* @param {Vector} param0.tile
* @param {number} param0.rotation
* @param {string} param0.variant
* @param {string} param0.layer
* @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array<Entity> }}
*/
computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) {
const connections = root.logic.getLocalWireConnectionsAtTile(tile);
const d = rotateDirectionalObject(connections, rotation);
// "Sticky" bottom
connections.bottom = true;
let flag = 0;
flag |= d.top ? 0x1000 : 0;
flag |= d.right ? 0x100 : 0;
flag |= d.bottom ? 0x10 : 0;
flag |= d.left ? 0x1 : 0;
let targetType = enumWireType.regular;
switch (flag) {
case 0x0000:
// Nothing
break;
case 0x0001:
// Left
targetType = enumWireType.turn;
rotation += 90;
break;
case 0x0010:
// Bottom
break;
case 0x0011:
// Bottom | Left
targetType = enumWireType.turn;
rotation += 90;
break;
case 0x0100:
// Right
targetType = enumWireType.turn;
break;
case 0x0101:
// Right | Left
// @todo: Might want to do rotation += 90 here
rotation += 90;
break;
case 0x0110:
// Right | Bottom
targetType = enumWireType.turn;
break;
case 0x0111:
// Right | Bottom | Left
targetType = enumWireType.split;
break;
case 0x1000:
// Top
break;
case 0x1001:
// Top | Left
targetType = enumWireType.turn;
rotation += 180;
break;
case 0x1010:
// Top | Bottom
break;
case 0x1011:
// Top | Bottom | Left
targetType = enumWireType.split;
rotation += 90;
break;
case 0x1100:
// Top | Right
targetType = enumWireType.turn;
rotation -= 90;
break;
case 0x1101:
// Top | Right | Left
targetType = enumWireType.split;
rotation += 180;
break;
case 0x1110:
// Top | Right | Bottom
targetType = enumWireType.split;
rotation -= 90;
break;
case 0x1111:
// Top | Right | Bottom | Left
// @todo: Crossing
break;
}
return {
// Clamp rotation
rotation: (rotation + 360 * 10) % 360,
rotationVariant: arrayWireRotationVariantToType.indexOf(targetType),
};
}
}

View File

@@ -18,10 +18,43 @@ export class WireComponent extends Component {
/**
* @param {object} param0
* @param {enumWireType?} param0.type
* @param {enumWireType=} param0.type
*/
constructor({ type = enumWireType.regular }) {
super();
this.type = type;
}
/**
* Returns the local connections
* @returns {import("../../core/utils").DirectionalObject}
*/
getLocalConnections() {
switch (this.type) {
case enumWireType.regular:
return {
top: true,
right: false,
bottom: true,
left: false,
};
case enumWireType.turn:
return {
top: false,
right: true,
bottom: true,
left: false,
};
case enumWireType.split:
return {
top: false,
right: true,
bottom: true,
left: true,
};
default:
assertAlways(false, "Invalid wire type: " + this.type);
}
}
}

View File

@@ -1,5 +1,5 @@
import { createLogger } from "../core/logging";
import { round2Digits } from "../core/utils";
import { round2Digits, rotateDirectionalObject } from "../core/utils";
import { enumDirection, enumDirectionToVector, Vector } from "../core/vector";
import { Entity } from "./entity";
import { MetaBuilding } from "./meta_building";
@@ -179,6 +179,46 @@ export class GameLogic {
return true;
}
/**
* Returns the wire connections at the given tile
* @param {Vector} tile
* @returns {import("../core/utils").DirectionalObject}
*/
getLocalWireConnectionsAtTile(tile) {
return {
top: this.getTileWireConnections(tile.addScalars(0, -1)).bottom,
right: this.getTileWireConnections(tile.addScalars(1, 0)).left,
bottom: this.getTileWireConnections(tile.addScalars(0, 1)).top,
left: this.getTileWireConnections(tile.addScalars(-1, 0)).right,
};
}
/**
* Returns the wire connection at the given tile
* @param {Vector} tile
* @returns {import("../core/utils").DirectionalObject}
*/
getTileWireConnections(tile) {
const result = {
top: null,
right: null,
bottom: null,
left: null,
};
const contents = this.root.map.getLayerContentXY(tile.x, tile.y, enumLayer.wires);
if (!contents) {
return result;
}
const staticComp = contents.components.StaticMapEntity;
const wiresComp = contents.components.Wire;
if (wiresComp) {
const connections = wiresComp.getLocalConnections();
return rotateDirectionalObject(connections, staticComp.rotation);
}
return result;
}
/**
* Returns the acceptors and ejectors which affect the current tile
* @param {Vector} tile

View File

@@ -3,6 +3,11 @@ import { WireComponent, enumWireType } from "../components/wire";
import { MapChunkView } from "../map_chunk_view";
import { globalConfig } from "../../core/config";
import { Loader } from "../../core/loader";
import { Entity } from "../entity";
import { gMetaBuildingRegistry } from "../../core/global_registries";
import { MetaWireBuilding, arrayWireRotationVariantToType } from "../buildings/wire";
import { Vector } from "../../core/vector";
import { defaultBuildingVariant } from "../meta_building";
export class WireSystem extends GameSystemWithFilter {
constructor(root) {
@@ -13,6 +18,9 @@ export class WireSystem extends GameSystemWithFilter {
[enumWireType.turn]: Loader.getSprite("sprites/buildings/wire-turn.png"),
[enumWireType.split]: Loader.getSprite("sprites/buildings/wire-split.png"),
};
this.root.signals.entityDestroyed.add(this.updateSurroundingWirePlacement, this);
this.root.signals.entityAdded.add(this.updateSurroundingWirePlacement, this);
}
/**
@@ -33,4 +41,70 @@ export class WireSystem extends GameSystemWithFilter {
}
}
}
/**
* Updates the wire placement after an entity has been added / deleted
* @param {Entity} entity
*/
updateSurroundingWirePlacement(entity) {
if (!this.root.gameInitialized) {
return;
}
const staticComp = entity.components.StaticMapEntity;
if (!staticComp) {
return;
}
const metaWire = gMetaBuildingRegistry.findByClass(MetaWireBuilding);
// Compute affected area
const originalRect = staticComp.getTileSpaceBounds();
const affectedArea = originalRect.expandedInAllDirections(1);
for (let x = affectedArea.x; x < affectedArea.right(); ++x) {
for (let y = affectedArea.y; y < affectedArea.bottom(); ++y) {
if (originalRect.containsPoint(x, y)) {
// Make sure we don't update the original entity
continue;
}
const targetEntities = this.root.map.getLayersContentsMultipleXY(x, y);
for (let i = 0; i < targetEntities.length; ++i) {
const targetEntity = targetEntities[i];
const targetWireComp = targetEntity.components.Wire;
const targetStaticComp = targetEntity.components.StaticMapEntity;
if (!targetWireComp) {
// Not a wire
continue;
}
const {
rotation,
rotationVariant,
} = metaWire.computeOptimalDirectionAndRotationVariantAtTile({
root: this.root,
tile: new Vector(x, y),
rotation: targetStaticComp.originalRotation,
variant: defaultBuildingVariant,
layer: targetEntity.layer,
});
// Compute delta to see if anything changed
const newType = arrayWireRotationVariantToType[rotationVariant];
if (targetStaticComp.rotation !== rotation || newType !== targetWireComp.type) {
// Change stuff
targetStaticComp.rotation = rotation;
metaWire.updateVariants(targetEntity, rotationVariant, defaultBuildingVariant);
// Make sure the chunks know about the update
this.root.signals.entityChanged.dispatch(targetEntity);
}
}
}
}
}
}