Fix wires placement not snapping

This commit is contained in:
tobspr 2020-06-30 08:23:05 +02:00
parent d62b95e974
commit 989ed2db87
10 changed files with 154 additions and 302 deletions

View File

@ -0,0 +1,52 @@
import { Loader } from "../../core/loader";
import { enumDirection } from "../../core/vector";
import { SOUNDS } from "../../platform/sound";
import { arrayBeltVariantToRotation, MetaBeltBaseBuilding } from "./belt_base";
export class MetaBeltBuilding extends MetaBeltBaseBuilding {
constructor() {
super("belt");
}
getSilhouetteColor() {
return "#777";
}
getPlacementSound() {
return SOUNDS.placeBelt;
}
getPreviewSprite(rotationVariant) {
switch (arrayBeltVariantToRotation[rotationVariant]) {
case enumDirection.top: {
return Loader.getSprite("sprites/buildings/belt_top.png");
}
case enumDirection.left: {
return Loader.getSprite("sprites/buildings/belt_left.png");
}
case enumDirection.right: {
return Loader.getSprite("sprites/buildings/belt_right.png");
}
default: {
assertAlways(false, "Invalid belt rotation variant");
}
}
}
getBlueprintSprite(rotationVariant) {
switch (arrayBeltVariantToRotation[rotationVariant]) {
case enumDirection.top: {
return Loader.getSprite("sprites/blueprints/belt_top.png");
}
case enumDirection.left: {
return Loader.getSprite("sprites/blueprints/belt_left.png");
}
case enumDirection.right: {
return Loader.getSprite("sprites/blueprints/belt_right.png");
}
default: {
assertAlways(false, "Invalid belt rotation variant");
}
}
}
}

View File

@ -14,14 +14,6 @@ import { GameRoot, enumLayer } from "../root";
export const arrayBeltVariantToRotation = [enumDirection.top, enumDirection.left, enumDirection.right];
export class MetaBeltBaseBuilding extends MetaBuilding {
constructor() {
super("belt");
}
getSilhouetteColor() {
return "#777";
}
getHasDirectionLockAvailable() {
return true;
}
@ -33,44 +25,9 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
*/
getAdditionalStatistics(root, variant) {
const beltSpeed = root.hubGoals.getBeltBaseSpeed();
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]];
}
getPreviewSprite(rotationVariant) {
switch (arrayBeltVariantToRotation[rotationVariant]) {
case enumDirection.top: {
return Loader.getSprite("sprites/buildings/belt_top.png");
}
case enumDirection.left: {
return Loader.getSprite("sprites/buildings/belt_left.png");
}
case enumDirection.right: {
return Loader.getSprite("sprites/buildings/belt_right.png");
}
default: {
assertAlways(false, "Invalid belt rotation variant");
}
}
}
getBlueprintSprite(rotationVariant) {
switch (arrayBeltVariantToRotation[rotationVariant]) {
case enumDirection.top: {
return Loader.getSprite("sprites/blueprints/belt_top.png");
}
case enumDirection.left: {
return Loader.getSprite("sprites/blueprints/belt_left.png");
}
case enumDirection.right: {
return Loader.getSprite("sprites/blueprints/belt_right.png");
}
default: {
assertAlways(false, "Invalid belt rotation variant");
}
}
}
getStayInPlacementMode() {
return true;
}
@ -93,6 +50,8 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
direction: enumDirection.top, // updated later
})
);
// Make this entity replaceabel
entity.addComponent(new ReplaceableMapEntityComponent());
entity.addComponent(
new ItemAcceptorComponent({
@ -100,6 +59,7 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
{
pos: new Vector(0, 0),
directions: [enumDirection.bottom],
layer: this.getLayer(),
},
],
animated: false,
@ -112,13 +72,12 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
{
pos: new Vector(0, 0),
direction: enumDirection.top, // updated later
layer: this.getLayer(),
},
],
instantEject: true,
})
);
// Make this entity replaceabel
entity.addComponent(new ReplaceableMapEntityComponent());
}
/**
@ -133,20 +92,22 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
}
/**
* Computes optimal belt rotation variant
* @param {GameRoot} root
* @param {Vector} tile
* @param {number} rotation
* @param {string} variant
* @return {{ rotation: number, rotationVariant: number }}
* 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) {
computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) {
const topDirection = enumAngleToDirection[rotation];
const rightDirection = enumAngleToDirection[(rotation + 90) % 360];
const bottomDirection = enumAngleToDirection[(rotation + 180) % 360];
const leftDirection = enumAngleToDirection[(rotation + 270) % 360];
const { ejectors, acceptors } = root.logic.getEjectorsAndAcceptorsAtTile(tile, enumLayer.regular);
const { ejectors, acceptors } = root.logic.getEjectorsAndAcceptorsAtTile(tile, layer);
let hasBottomEjector = false;
let hasRightEjector = false;

View File

@ -131,13 +131,16 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding {
}
/**
* @param {GameRoot} root
* @param {Vector} tile
* @param {number} rotation
* @param {string} 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) {
computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) {
const searchDirection = enumAngleToDirection[rotation];
const searchVector = enumDirectionToVector[searchDirection];
const tier = enumUndergroundBeltVariantToTier[variant];

View File

@ -1,17 +1,9 @@
import { Loader } from "../../core/loader";
import { enumAngleToDirection, enumDirection, Vector } from "../../core/vector";
import { SOUNDS } from "../../platform/sound";
import { BeltComponent } from "../components/belt";
import { ItemAcceptorComponent } from "../components/item_acceptor";
import { ItemEjectorComponent } from "../components/item_ejector";
import { ReplaceableMapEntityComponent } from "../components/replaceable_map_entity";
import { Entity } from "../entity";
import { MetaBuilding } from "../meta_building";
import { enumLayer, GameRoot } from "../root";
import { enumDirection } from "../../core/vector";
import { enumLayer } from "../root";
import { arrayBeltVariantToRotation, MetaBeltBaseBuilding } from "./belt_base";
export const arrayBeltVariantToRotation = [enumDirection.top, enumDirection.left, enumDirection.right];
export class MetaWireBaseBuilding extends MetaBuilding {
export class MetaWireBaseBuilding extends MetaBeltBaseBuilding {
constructor() {
super("wire");
}
@ -24,10 +16,6 @@ export class MetaWireBaseBuilding extends MetaBuilding {
return enumLayer.wires;
}
getHasDirectionLockAvailable() {
return true;
}
getPreviewSprite(rotationVariant) {
switch (arrayBeltVariantToRotation[rotationVariant]) {
case enumDirection.top: {
@ -61,166 +49,4 @@ export class MetaWireBaseBuilding extends MetaBuilding {
}
}
}
getStayInPlacementMode() {
return true;
}
getRotateAutomaticallyWhilePlacing() {
return true;
}
getPlacementSound() {
return SOUNDS.placeBelt;
}
/**
* Creates the entity at the given location
* @param {Entity} entity
*/
setupEntityComponents(entity) {
entity.addComponent(
new BeltComponent({
direction: enumDirection.top, // updated later
})
);
entity.addComponent(
new ItemAcceptorComponent({
slots: [
{
pos: new Vector(0, 0),
directions: [enumDirection.bottom],
layer: enumLayer.wires,
},
],
animated: false,
})
);
entity.addComponent(
new ItemEjectorComponent({
slots: [
{
pos: new Vector(0, 0),
direction: enumDirection.top, // updated later
layer: enumLayer.wires,
},
],
instantEject: true,
})
);
// Make this entity replaceable
entity.addComponent(new ReplaceableMapEntityComponent());
}
/**
* @param {Entity} entity
* @param {number} rotationVariant
*/
updateVariants(entity, rotationVariant) {
entity.components.Belt.direction = arrayBeltVariantToRotation[rotationVariant];
entity.components.ItemEjector.slots[0].direction = arrayBeltVariantToRotation[rotationVariant];
entity.components.StaticMapEntity.spriteKey = null;
}
/**
* Computes optimal belt rotation variant
* @param {GameRoot} root
* @param {Vector} tile
* @param {number} rotation
* @param {string} variant
* @return {{ rotation: number, rotationVariant: number }}
*/
computeOptimalDirectionAndRotationVariantAtTile(root, tile, rotation, variant) {
const topDirection = enumAngleToDirection[rotation];
const rightDirection = enumAngleToDirection[(rotation + 90) % 360];
const bottomDirection = enumAngleToDirection[(rotation + 180) % 360];
const leftDirection = enumAngleToDirection[(rotation + 270) % 360];
const { ejectors, acceptors } = root.logic.getEjectorsAndAcceptorsAtTile(tile, enumLayer.wires);
let hasBottomEjector = false;
let hasRightEjector = false;
let hasLeftEjector = false;
let hasTopAcceptor = false;
let hasLeftAcceptor = false;
let hasRightAcceptor = false;
// Check all ejectors
for (let i = 0; i < ejectors.length; ++i) {
const ejector = ejectors[i];
if (ejector.toDirection === topDirection) {
hasBottomEjector = true;
} else if (ejector.toDirection === leftDirection) {
hasRightEjector = true;
} else if (ejector.toDirection === rightDirection) {
hasLeftEjector = true;
}
}
// Check all acceptors
for (let i = 0; i < acceptors.length; ++i) {
const acceptor = acceptors[i];
if (acceptor.fromDirection === bottomDirection) {
hasTopAcceptor = true;
} else if (acceptor.fromDirection === rightDirection) {
hasLeftAcceptor = true;
} else if (acceptor.fromDirection === leftDirection) {
hasRightAcceptor = true;
}
}
// Soo .. if there is any ejector below us we always prioritize
// this ejector
if (!hasBottomEjector) {
// When something ejects to us from the left and nothing from the right,
// do a curve from the left to the top
if (hasRightEjector && !hasLeftEjector) {
return {
rotation: (rotation + 270) % 360,
rotationVariant: 2,
};
}
// When something ejects to us from the right and nothing from the left,
// do a curve from the right to the top
if (hasLeftEjector && !hasRightEjector) {
return {
rotation: (rotation + 90) % 360,
rotationVariant: 1,
};
}
}
// When there is a top acceptor, ignore sides
// NOTICE: This makes the belt prefer side turns *way* too much!
if (!hasTopAcceptor) {
// When there is an acceptor to the right but no acceptor to the left,
// do a turn to the right
if (hasRightAcceptor && !hasLeftAcceptor) {
return {
rotation,
rotationVariant: 2,
};
}
// When there is an acceptor to the left but no acceptor to the right,
// do a turn to the left
if (hasLeftAcceptor && !hasRightAcceptor) {
return {
rotation,
rotationVariant: 1,
};
}
}
return {
rotation,
rotationVariant: 0,
};
}
}

View File

@ -230,12 +230,13 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
rotation,
rotationVariant,
connectedEntities,
} = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile(
this.root,
mouseTile,
this.currentBaseRotation,
this.currentVariant.get()
);
} = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile({
root: this.root,
tile: mouseTile,
rotation: this.currentBaseRotation,
variant: this.currentVariant.get(),
layer: metaBuilding.getLayer(),
});
// Check if there are connected entities
if (connectedEntities) {
@ -411,12 +412,15 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
const worldDirection = staticComp.localDirectionToWorld(direction);
const sourceTile = acceptorSlotWsTile.add(enumDirectionToVector[worldDirection]);
const sourceEntity = this.root.map.getTileContent(sourceTile, this.root.currentLayer);
let sprite = goodArrowSprite;
let alpha = 0.5;
let alpha = 0.3;
if (sourceEntity) {
const sourceEntities = this.root.map.getLayersContentsMultipleXY(
sourceTile.x,
sourceTile.y
);
for (let i = 0; i < sourceEntities.length; ++i) {
const sourceEntity = sourceEntities[i];
sprite = badArrowSprite;
const sourceEjector = sourceEntity.components.ItemEjector;
const sourceStaticComp = sourceEntity.components.StaticMapEntity;
@ -462,15 +466,19 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
const ejectorSLotWsPos = ejectorSlotWsTile.toWorldSpaceCenterOfTile();
const ejectorSlotWsDirection = staticComp.localDirectionToWorld(slot.direction);
const destEntity = this.root.map.getTileContent(ejectorSlotWsTile, this.root.currentLayer);
let sprite = goodArrowSprite;
let alpha = 0.5;
if (destEntity) {
let alpha = 0.3;
const destEntities = this.root.map.getLayersContentsMultipleXY(
ejectorSlotWsTile.x,
ejectorSlotWsTile.y
);
for (let i = 0; i < destEntities.length; ++i) {
alpha = 1;
const destEntity = destEntities[i];
const destAcceptor = destEntity.components.ItemAcceptor;
const destStaticComp = destEntity.components.StaticMapEntity;
if (destAcceptor) {
const destLocalTile = destStaticComp.worldToLocalTile(ejectorSlotWsTile);
const destLocalDir = destStaticComp.worldDirectionToLocal(ejectorSlotWsDirection);

View File

@ -444,12 +444,13 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart {
}
const metaBuilding = this.currentMetaBuilding.get();
const { rotation, rotationVariant } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile(
this.root,
const { rotation, rotationVariant } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile({
root: this.root,
tile,
this.currentBaseRotation,
this.currentVariant.get()
);
rotation: this.currentBaseRotation,
variant: this.currentVariant.get(),
layer: metaBuilding.getLayer(),
});
const entity = this.root.logic.tryPlaceBuilding({
origin: tile,

View File

@ -272,8 +272,10 @@ export class GameLogic {
continue;
}
const entity = this.root.map.getLayerContentXY(tile.x + dx, tile.y + dy, layer);
if (entity) {
const entities = this.root.map.getLayersContentsMultipleXY(tile.x + dx, tile.y + dy);
for (let i = 0; i < entities.length; ++i) {
const entity = entities[i];
const staticComp = entity.components.StaticMapEntity;
const itemEjector = entity.components.ItemEjector;
if (itemEjector) {

View File

@ -204,13 +204,15 @@ export class MetaBuilding {
/**
* Should compute the optimal rotation variant on the given tile
* @param {GameRoot} root
* @param {Vector} tile
* @param {number} rotation
* @param {string} variant
* @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) {
computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) {
if (!this.isRotateable(variant)) {
return {
rotation: 0,

View File

@ -13,6 +13,7 @@ import { MetaHubBuilding } from "./buildings/hub";
import { MetaEnergyGenerator } from "./buildings/energy_generator";
import { MetaWireBaseBuilding } from "./buildings/wire_base";
import { MetaAdvancedProcessorBuilding } from "./buildings/advanced_processor";
import { MetaBeltBuilding } from "./buildings/belt";
export function initMetaBuildingRegistry() {
gMetaBuildingRegistry.register(MetaSplitterBuilding);
@ -23,7 +24,7 @@ export function initMetaBuildingRegistry() {
gMetaBuildingRegistry.register(MetaMixerBuilding);
gMetaBuildingRegistry.register(MetaPainterBuilding);
gMetaBuildingRegistry.register(MetaTrashBuilding);
gMetaBuildingRegistry.register(MetaBeltBaseBuilding);
gMetaBuildingRegistry.register(MetaBeltBuilding);
gMetaBuildingRegistry.register(MetaUndergroundBeltBuilding);
gMetaBuildingRegistry.register(MetaHubBuilding);
gMetaBuildingRegistry.register(MetaEnergyGenerator);

View File

@ -132,11 +132,7 @@ export class BeltSystem extends GameSystemWithFilter {
return;
}
/* BIG HACK: We don't actually store the meta building */
const metaBelt = gMetaBuildingRegistry.findByClass(
entity.layer === enumLayer.regular ? MetaBeltBaseBuilding : MetaWireBaseBuilding
);
const metaBelt = gMetaBuildingRegistry.findByClass(MetaBeltBaseBuilding);
// Compute affected area
const originalRect = staticComp.getTileSpaceBounds();
const affectedArea = originalRect.expandedInAllDirections(1);
@ -148,47 +144,47 @@ export class BeltSystem extends GameSystemWithFilter {
continue;
}
const targetEntity = this.root.map.getLayerContentXY(x, y, entity.layer);
if (!targetEntity) {
// Empty tile
continue;
}
const targetEntities = this.root.map.getLayersContentsMultipleXY(x, y, entity.layer);
for (let i = 0; i < targetEntities.length; ++i) {
const targetEntity = targetEntities[i];
const targetBeltComp = targetEntity.components.Belt;
const targetStaticComp = targetEntity.components.StaticMapEntity;
const targetBeltComp = targetEntity.components.Belt;
const targetStaticComp = targetEntity.components.StaticMapEntity;
if (!targetBeltComp) {
// Not a belt
continue;
}
if (!targetBeltComp) {
// Not a belt
continue;
}
const {
rotation,
rotationVariant,
} = metaBelt.computeOptimalDirectionAndRotationVariantAtTile(
this.root,
new Vector(x, y),
targetStaticComp.originalRotation,
defaultBuildingVariant
);
const {
rotation,
rotationVariant,
} = metaBelt.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 newDirection = arrayBeltVariantToRotation[rotationVariant];
// Compute delta to see if anything changed
const newDirection = arrayBeltVariantToRotation[rotationVariant];
if (targetStaticComp.rotation !== rotation || newDirection !== targetBeltComp.direction) {
// Ok, first remove it from its current path
this.deleteEntityFromPath(targetBeltComp.assignedPath, targetEntity);
if (targetStaticComp.rotation !== rotation || newDirection !== targetBeltComp.direction) {
// Ok, first remove it from its current path
this.deleteEntityFromPath(targetBeltComp.assignedPath, targetEntity);
// Change stuff
targetStaticComp.rotation = rotation;
metaBelt.updateVariants(targetEntity, rotationVariant, defaultBuildingVariant);
// Change stuff
targetStaticComp.rotation = rotation;
metaBelt.updateVariants(targetEntity, rotationVariant, defaultBuildingVariant);
// Now add it again
this.addEntityToPaths(targetEntity);
// Now add it again
this.addEntityToPaths(targetEntity);
// Sanity
if (G_IS_DEV && globalConfig.debug.checkBeltPaths) {
this.debug_verifyBeltPaths();
// Sanity
if (G_IS_DEV && globalConfig.debug.checkBeltPaths) {
this.debug_verifyBeltPaths();
}
}
}
}