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:
@@ -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],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user