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

Improve wire auto-connect

This commit is contained in:
tobspr
2020-08-12 20:11:24 +02:00
parent f16ab2389a
commit f65b3728ed
34 changed files with 1051 additions and 804 deletions

View File

@@ -1,35 +1,24 @@
import { Loader } from "../../core/loader";
import { rotateDirectionalObject } from "../../core/utils";
import { Vector } from "../../core/vector";
import { generateMatrixRotations } from "../../core/utils";
import { enumDirection, enumDirectionToAngle, enumDirectionToVector, 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";
export const arrayWireRotationVariantToType = [enumWireType.regular, enumWireType.turn, enumWireType.split];
export const arrayWireRotationVariantToType = [
enumWireType.regular,
enumWireType.turn,
enumWireType.split,
enumWireType.cross,
];
export const wireOverlayMatrices = {
[enumWireType.regular]: {
0: [0, 1, 0, 0, 1, 0, 0, 1, 0],
90: [0, 0, 0, 1, 1, 1, 0, 0, 0],
180: [0, 1, 0, 0, 1, 0, 0, 1, 0],
270: [0, 0, 0, 1, 1, 1, 0, 0, 0],
},
[enumWireType.split]: {
0: [0, 0, 0, 1, 1, 1, 0, 1, 0],
90: [0, 1, 0, 1, 1, 0, 0, 1, 0],
180: [0, 1, 0, 1, 1, 1, 0, 0, 0],
270: [0, 1, 0, 0, 1, 1, 0, 1, 0],
},
[enumWireType.turn]: {
0: [0, 0, 0, 0, 1, 1, 0, 1, 0],
90: [0, 0, 0, 1, 1, 0, 0, 1, 0],
180: [0, 1, 0, 1, 1, 0, 0, 0, 0],
270: [0, 1, 0, 0, 1, 1, 0, 0, 0],
},
[enumWireType.regular]: generateMatrixRotations([0, 1, 0, 0, 1, 0, 0, 1, 0]),
[enumWireType.split]: generateMatrixRotations([0, 0, 0, 1, 1, 1, 0, 1, 0]),
[enumWireType.turn]: generateMatrixRotations([0, 0, 0, 0, 1, 1, 0, 1, 0]),
[enumWireType.cross]: generateMatrixRotations([0, 1, 0, 1, 1, 1, 0, 1, 0]),
};
export class MetaWireBuilding extends MetaBuilding {
@@ -121,8 +110,11 @@ export class MetaWireBuilding extends MetaBuilding {
case enumWireType.split: {
return Loader.getSprite("sprites/buildings/wire-split.png");
}
case enumWireType.cross: {
return Loader.getSprite("sprites/buildings/wire-cross.png");
}
default: {
assertAlways(false, "Invalid belt rotation variant");
assertAlways(false, "Invalid wire rotation variant");
}
}
}
@@ -138,8 +130,11 @@ export class MetaWireBuilding extends MetaBuilding {
case enumWireType.split: {
return Loader.getSprite("sprites/blueprints/wire-split.png");
}
case enumWireType.cross: {
return Loader.getSprite("sprites/blueprints/wire-cross.png");
}
default: {
assertAlways(false, "Invalid belt rotation variant");
assertAlways(false, "Invalid wire rotation variant");
}
}
}
@@ -155,21 +150,24 @@ export class MetaWireBuilding extends MetaBuilding {
* @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;
const connections = {
top: root.logic.computeWireEdgeStatus({ tile, rotation, edge: enumDirection.top }),
right: root.logic.computeWireEdgeStatus({ tile, rotation, edge: enumDirection.right }),
bottom: root.logic.computeWireEdgeStatus({ tile, rotation, edge: enumDirection.bottom }),
left: root.logic.computeWireEdgeStatus({ tile, rotation, edge: enumDirection.left }),
};
let flag = 0;
flag |= d.top ? 0x1000 : 0;
flag |= d.right ? 0x100 : 0;
flag |= d.bottom ? 0x10 : 0;
flag |= d.left ? 0x1 : 0;
flag |= connections.top ? 0x1000 : 0;
flag |= connections.right ? 0x100 : 0;
flag |= connections.bottom ? 0x10 : 0;
flag |= connections.left ? 0x1 : 0;
let targetType = enumWireType.regular;
// First, reset rotation
rotation = 0;
switch (flag) {
case 0x0000:
// Nothing
@@ -177,12 +175,12 @@ export class MetaWireBuilding extends MetaBuilding {
case 0x0001:
// Left
targetType = enumWireType.turn;
rotation += 90;
break;
case 0x0010:
// Bottom
// END
break;
case 0x0011:
@@ -193,12 +191,11 @@ export class MetaWireBuilding extends MetaBuilding {
case 0x0100:
// Right
targetType = enumWireType.turn;
rotation += 90;
break;
case 0x0101:
// Right | Left
// @todo: Might want to do rotation += 90 here
rotation += 90;
break;
@@ -252,7 +249,7 @@ export class MetaWireBuilding extends MetaBuilding {
case 0x1111:
// Top | Right | Bottom | Left
// @todo: Crossing
targetType = enumWireType.cross;
break;
}

View File

@@ -4,9 +4,8 @@ import { Rectangle } from "../../core/rectangle";
import { AtlasSprite } from "../../core/sprites";
import { enumDirection, Vector } from "../../core/vector";
import { types } from "../../savegame/serialization";
import { Component } from "../component";
import { getBuildingDataFromCode } from "../building_codes";
import { MetaBuilding } from "../meta_building";
import { Component } from "../component";
export class StaticMapEntityComponent extends Component {
static getId() {
@@ -58,7 +57,7 @@ export class StaticMapEntityComponent extends Component {
/**
* Returns the meta building
* @returns {MetaBuilding}
* @returns {import("../meta_building").MetaBuilding}
*/
getMetaBuilding() {
return getBuildingDataFromCode(this.code).metaInstance;

View File

@@ -5,6 +5,7 @@ export const enumWireType = {
regular: "regular",
turn: "turn",
split: "split",
cross: "cross",
};
export class WireComponent extends Component {
@@ -30,31 +31,38 @@ export class WireComponent extends Component {
* @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,
};
return {
top: true,
right: false,
bottom: true,
left: false,
};
default:
assertAlways(false, "Invalid wire type: " + this.type);
}
// 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,6 +1,6 @@
import { enumDirection, Vector } from "../../core/vector";
import { BaseItem } from "../base_item";
import { Component } from "../component";
import { Vector, enumDirection } from "../../core/vector";
import { types } from "../../savegame/serialization";
/** @enum {string} */
export const enumPinSlotType = {
@@ -17,7 +17,8 @@ export const enumPinSlotType = {
/** @typedef {{
* pos: Vector,
* type: enumPinSlotType,
* direction: enumDirection
* direction: enumDirection,
* value: BaseItem
* }} WirePinSlot */
export class WiredPinsComponent extends Component {
@@ -63,6 +64,7 @@ export class WiredPinsComponent extends Component {
pos: slotData.pos,
type: slotData.type,
direction: slotData.direction,
value: null,
});
}
}

View File

@@ -1,6 +1,12 @@
import { createLogger } from "../core/logging";
import { round2Digits, rotateDirectionalObject } from "../core/utils";
import { enumDirection, enumDirectionToVector, Vector } from "../core/vector";
import {
enumDirection,
enumDirectionToVector,
Vector,
enumDirectionToAngle,
enumInvertedDirections,
} from "../core/vector";
import { Entity } from "./entity";
import { MetaBuilding } from "./meta_building";
import { GameRoot, enumLayer } from "./root";
@@ -8,6 +14,13 @@ import { STOP_PROPAGATION } from "../core/signal";
const logger = createLogger("ingame/logic");
/** @enum {number} */
export const enumWireEdgeFlag = {
empty: 0,
filled: 1,
connected: 2,
};
/**
* Typing helper
* @typedef {Array<{
@@ -214,11 +227,111 @@ export class GameLogic {
const wiresComp = contents.components.Wire;
if (wiresComp) {
const connections = wiresComp.getLocalConnections();
return rotateDirectionalObject(connections, staticComp.rotation);
return rotateDirectionalObject(connections, staticComp.originalRotation);
}
return result;
}
/**
*
* Computes the flag for a given tile
* @param {object} param0
* @param {Vector} param0.tile The tile to check at
* @param {enumDirection} param0.edge The edge to check for
* @param {number} param0.rotation The local tiles base rotation
*/
computeWireEdgeStatus({ tile, edge, rotation }) {
const offset = enumDirectionToVector[edge];
const refTile = tile.add(offset);
const angle = enumDirectionToAngle[edge];
// // First, check if this edge can be connected from locally
// const canConnectLocally = rotation === angle || (rotation + 180) % 360 === angle;
const neighbourStatus = this.getWireEdgeFlag(refTile, edge);
if (neighbourStatus === enumWireEdgeFlag.empty) {
// It's empty, no point in connecting
return false;
}
if (neighbourStatus === enumWireEdgeFlag.filled) {
return true;
}
if (neighbourStatus === enumWireEdgeFlag.connected) {
return true;
}
}
/**
* Gets the flag at the given tile
* @param {Vector} tile
* @param {enumDirection} edge
* @returns {enumWireEdgeFlag}
*/
getWireEdgeFlag(tile, edge) {
// Search for relevant pins
const pinEntities = this.root.map.getLayersContentsMultipleXY(tile.x, tile.y);
// Go over all entities which could have a pin
for (let i = 0; i < pinEntities.length; ++i) {
const pinEntity = pinEntities[i];
const pinComp = pinEntity.components.WiredPins;
const staticComp = pinEntity.components.StaticMapEntity;
// Skip those who don't have pins
if (!pinComp) {
continue;
}
// Go over all pins
const pins = pinComp.slots;
for (let k = 0; k < pinComp.slots.length; ++k) {
const pinSlot = pins[k];
const pinLocation = staticComp.localTileToWorld(pinSlot.pos);
// Check if the pin has the right location
if (!pinLocation.equals(tile)) {
continue;
}
// Check if the pin has the right direction
if (pinSlot.direction !== enumInvertedDirections[edge]) {
continue;
}
// Found a pin!
return enumWireEdgeFlag.connected;
}
}
// Now check if there's a connectable wire
const targetEntity = this.root.map.getTileContent(tile, enumLayer.wires);
if (!targetEntity) {
return enumWireEdgeFlag.empty;
}
// Check if its a wire
const wiresComp = targetEntity.components.Wire;
if (!wiresComp) {
return enumWireEdgeFlag.empty;
}
const refAngle = enumDirectionToAngle[edge];
const refRotation = targetEntity.components.StaticMapEntity.originalRotation;
const canConnectRemotely = refRotation === refAngle || (refRotation + 180) % 360 === refAngle;
// Check if the wire points towards the right direction
if (!canConnectRemotely) {
// Seems its not the right direction - well, still its filled
return enumWireEdgeFlag.filled;
}
// Actually connected
return enumWireEdgeFlag.connected;
}
/**
* Returns the acceptors and ejectors which affect the current tile
* @param {Vector} tile

View File

@@ -84,6 +84,7 @@ export function initMetaBuildingRegistry() {
registerBuildingVariant(27, MetaWireBuilding, defaultBuildingVariant, 0);
registerBuildingVariant(28, MetaWireBuilding, defaultBuildingVariant, 1);
registerBuildingVariant(29, MetaWireBuilding, defaultBuildingVariant, 2);
registerBuildingVariant(30, MetaWireBuilding, defaultBuildingVariant, 3);
// Propagate instances
for (const key in gBuildingVariants) {

View File

@@ -5,6 +5,7 @@ import { Entity } from "../entity";
import { formatBigNumber } from "../../core/utils";
import { Loader } from "../../core/loader";
import { T } from "../../translations";
import { ShapeItem } from "../items/shape_item";
export class HubSystem extends GameSystemWithFilter {
constructor(root) {
@@ -17,7 +18,14 @@ export class HubSystem extends GameSystemWithFilter {
this.forEachMatchingEntityOnScreen(parameters, this.drawEntity.bind(this));
}
update() {}
update() {
for (let i = 0; i < this.allEntities.length; ++i) {
// Set hub goal
const entity = this.allEntities[i];
const pinsComp = entity.components.WiredPins;
pinsComp.slots[0].value = new ShapeItem(this.root.hubGoals.currentGoal.definition);
}
}
/**
* @param {DrawParameters} parameters

View File

@@ -17,6 +17,7 @@ export class WireSystem extends GameSystemWithFilter {
[enumWireType.regular]: Loader.getSprite("sprites/buildings/wire.png"),
[enumWireType.turn]: Loader.getSprite("sprites/buildings/wire-turn.png"),
[enumWireType.split]: Loader.getSprite("sprites/buildings/wire-split.png"),
[enumWireType.cross]: Loader.getSprite("sprites/buildings/wire-cross.png"),
};
this.root.signals.entityDestroyed.add(this.updateSurroundingWirePlacement, this);
@@ -36,7 +37,36 @@ export class WireSystem extends GameSystemWithFilter {
if (entity && entity.components.Wire) {
const wireType = entity.components.Wire.type;
const sprite = this.wireSprites[wireType];
entity.components.StaticMapEntity.drawSpriteOnFullEntityBounds(parameters, sprite, 0);
assert(sprite, "Unknown wire type: " + wireType);
const staticComp = entity.components.StaticMapEntity;
staticComp.drawSpriteOnFullEntityBounds(parameters, sprite, 0);
if (G_IS_DEV && globalConfig.debug.renderWireRotations) {
parameters.context.fillStyle = "red";
parameters.context.font = "5px Tahoma";
parameters.context.fillText(
"" + staticComp.originalRotation,
staticComp.origin.x * globalConfig.tileSize,
staticComp.origin.y * globalConfig.tileSize + 5
);
parameters.context.fillStyle = "rgba(255, 0, 0, 0.2)";
if (staticComp.originalRotation % 180 === 0) {
parameters.context.fillRect(
(staticComp.origin.x + 0.5) * globalConfig.tileSize,
staticComp.origin.y * globalConfig.tileSize,
3,
globalConfig.tileSize
);
} else {
parameters.context.fillRect(
staticComp.origin.x * globalConfig.tileSize,
(staticComp.origin.y + 0.5) * globalConfig.tileSize,
globalConfig.tileSize,
3
);
}
}
}
}
}

View File

@@ -179,6 +179,12 @@ export class WiredPinsSystem extends GameSystemWithFilter {
offsetX: 0,
offsetY: 0,
});
// Draw contained item to visualize whats emitted
const value = slot.value;
if (value) {
value.draw(worldPos.x, worldPos.y, parameters, 12);
}
}
}
}