diff --git a/src/js/core/vector.js b/src/js/core/vector.js index 635556d6..34935064 100644 --- a/src/js/core/vector.js +++ b/src/js/core/vector.js @@ -519,6 +519,49 @@ export class Vector { } } + /** + * Mirrors this vector + * @param {boolean} mirror + * @returns {Vector} new vector + */ + mirror(mirror = true) { + if (mirror) { + return new Vector(-this.x, this.y); + } + else { + return new Vector(this.x, this.y); + } + } + + /** + * Helper method to mirror a direction + * @param {enumDirection} direction + * @param {boolean} mirror + * @returns {enumDirection} + */ + static mirrorDirection(direction, mirror = true) { + if (mirror) { + switch (direction) { + case enumDirection.left: return enumDirection.right; + case enumDirection.right: return enumDirection.left; + } + } + return direction; + } + + /** + * Helper method to mirror an angle + * @param {number} angle + * @param {boolean} mirror + * @returns {number} + */ + static mirrorAngle(angle, mirror = true) { + if (mirror && (angle == 90 || angle == 270) ) { + return 360 - angle; + } + return angle; + } + /** * Helper method to rotate a direction * @param {enumDirection} direction diff --git a/src/js/game/buildings/belt_base.js b/src/js/game/buildings/belt_base.js index 10174943..1d64c23c 100644 --- a/src/js/game/buildings/belt_base.js +++ b/src/js/game/buildings/belt_base.js @@ -141,7 +141,7 @@ export class MetaBeltBaseBuilding extends MetaBuilding { * @param {string} variant * @return {{ rotation: number, rotationVariant: number }} */ - computeOptimalDirectionAndRotationVariantAtTile(root, tile, rotation, variant) { + computeOptimalDirectionAndRotationVariantAtTile(root, tile, rotation, mirrored, variant) { const topDirection = enumAngleToDirection[rotation]; const rightDirection = enumAngleToDirection[(rotation + 90) % 360]; const bottomDirection = enumAngleToDirection[(rotation + 180) % 360]; @@ -191,6 +191,7 @@ export class MetaBeltBaseBuilding extends MetaBuilding { if (hasRightEjector && !hasLeftEjector) { return { rotation: (rotation + 270) % 360, + mirrored: false, rotationVariant: 2, }; } @@ -200,6 +201,7 @@ export class MetaBeltBaseBuilding extends MetaBuilding { if (hasLeftEjector && !hasRightEjector) { return { rotation: (rotation + 90) % 360, + mirrored: false, rotationVariant: 1, }; } @@ -213,6 +215,7 @@ export class MetaBeltBaseBuilding extends MetaBuilding { if (hasRightAcceptor && !hasLeftAcceptor) { return { rotation, + mirrored: false, rotationVariant: 2, }; } @@ -222,6 +225,7 @@ export class MetaBeltBaseBuilding extends MetaBuilding { if (hasLeftAcceptor && !hasRightAcceptor) { return { rotation, + mirrored: false, rotationVariant: 1, }; } @@ -229,6 +233,7 @@ export class MetaBeltBaseBuilding extends MetaBuilding { return { rotation, + mirrored: false, rotationVariant: 0, }; } diff --git a/src/js/game/buildings/hub.js b/src/js/game/buildings/hub.js index 49d95005..b5d248a6 100644 --- a/src/js/game/buildings/hub.js +++ b/src/js/game/buildings/hub.js @@ -24,6 +24,10 @@ export class MetaHubBuilding extends MetaBuilding { return false; } + isMirrorable(variant) { + return false; + } + getBlueprintSprite() { return null; } diff --git a/src/js/game/buildings/rotater.js b/src/js/game/buildings/rotater.js index abd3f2c2..cc0512b2 100644 --- a/src/js/game/buildings/rotater.js +++ b/src/js/game/buildings/rotater.js @@ -18,6 +18,10 @@ export class MetaRotaterBuilding extends MetaBuilding { super("rotater"); } + isMirrorable(variant) { + return false; + } + getSilhouetteColor() { return "#7dc6cd"; } diff --git a/src/js/game/buildings/trash.js b/src/js/game/buildings/trash.js index 8ae36b8d..a01bc6ee 100644 --- a/src/js/game/buildings/trash.js +++ b/src/js/game/buildings/trash.js @@ -24,6 +24,10 @@ export class MetaTrashBuilding extends MetaBuilding { return variant !== defaultBuildingVariant; } + isMirrorable(variant) { + return variant !== defaultBuildingVariant; + } + getSilhouetteColor() { return "#cd7d86"; } diff --git a/src/js/game/buildings/underground_belt.js b/src/js/game/buildings/underground_belt.js index 6d24267b..2e1c9d68 100644 --- a/src/js/game/buildings/underground_belt.js +++ b/src/js/game/buildings/underground_belt.js @@ -137,7 +137,7 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding { * @param {string} variant * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array }} */ - computeOptimalDirectionAndRotationVariantAtTile(root, tile, rotation, variant) { + computeOptimalDirectionAndRotationVariantAtTile(root, tile, rotation, mirrored, variant) { const searchDirection = enumAngleToDirection[rotation]; const searchVector = enumDirectionToVector[searchDirection]; const tier = enumUndergroundBeltVariantToTier[variant]; @@ -164,6 +164,7 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding { } return { rotation: targetRotation, + mirrored: false, rotationVariant: 1, connectedEntities: [contents], }; @@ -172,6 +173,7 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding { if (undergroundComp.mode === enumUndergroundBeltMode.receiver) { return { rotation: rotation, + mirrored: false, rotationVariant: 0, connectedEntities: [contents], }; @@ -185,6 +187,7 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding { return { rotation, + mirrored: false, rotationVariant: 0, }; } diff --git a/src/js/game/components/static_map_entity.js b/src/js/game/components/static_map_entity.js index ed616213..af966973 100644 --- a/src/js/game/components/static_map_entity.js +++ b/src/js/game/components/static_map_entity.js @@ -18,6 +18,7 @@ export class StaticMapEntityComponent extends Component { tileSize: types.tileVector, rotation: types.float, originalRotation: types.float, + mirrored: types.bool, spriteKey: types.nullable(types.string), blueprintSpriteKey: types.string, silhouetteColor: types.nullable(types.string), @@ -30,6 +31,7 @@ export class StaticMapEntityComponent extends Component { tileSize: this.tileSize.copy(), rotation: this.rotation, originalRotation: this.originalRotation, + mirrored: this.mirrored, spriteKey: this.spriteKey, silhouetteColor: this.silhouetteColor, blueprintSpriteKey: this.blueprintSpriteKey, @@ -52,6 +54,7 @@ export class StaticMapEntityComponent extends Component { tileSize = new Vector(1, 1), rotation = 0, originalRotation = 0, + mirrored = false, spriteKey = null, silhouetteColor = null, blueprintSpriteKey = null, @@ -67,6 +70,7 @@ export class StaticMapEntityComponent extends Component { this.spriteKey = spriteKey; this.rotation = rotation; this.originalRotation = originalRotation; + this.mirrored = mirrored; this.silhouetteColor = silhouetteColor; this.blueprintSpriteKey = blueprintSpriteKey; } @@ -76,32 +80,62 @@ export class StaticMapEntityComponent extends Component { * @returns {Rectangle} */ getTileSpaceBounds() { - switch (this.rotation) { - case 0: - return new Rectangle(this.origin.x, this.origin.y, this.tileSize.x, this.tileSize.y); - case 90: - return new Rectangle( - this.origin.x - this.tileSize.y + 1, - this.origin.y, - this.tileSize.y, - this.tileSize.x - ); - case 180: - return new Rectangle( - this.origin.x - this.tileSize.x + 1, - this.origin.y - this.tileSize.y + 1, - this.tileSize.x, - this.tileSize.y - ); - case 270: - return new Rectangle( - this.origin.x, - this.origin.y - this.tileSize.x + 1, - this.tileSize.y, - this.tileSize.x - ); - default: - assert(false, "Invalid rotation"); + if (!this.mirrored) { + switch (this.rotation) { + case 0: + return new Rectangle(this.origin.x, this.origin.y, this.tileSize.x, this.tileSize.y); + case 90: + return new Rectangle( + this.origin.x - this.tileSize.y + 1, + this.origin.y, + this.tileSize.y, + this.tileSize.x + ); + case 180: + return new Rectangle( + this.origin.x - this.tileSize.x + 1, + this.origin.y - this.tileSize.y + 1, + this.tileSize.x, + this.tileSize.y + ); + case 270: + return new Rectangle( + this.origin.x, + this.origin.y - this.tileSize.x + 1, + this.tileSize.y, + this.tileSize.x + ); + default: + assert(false, "Invalid rotation"); + } + } else { + switch (this.rotation) { + case 0: + return new Rectangle(this.origin.x - this.tileSize.x + 1, this.origin.y, this.tileSize.x, this.tileSize.y); + case 90: + return new Rectangle( + this.origin.x - this.tileSize.y + 1, + this.origin.y - this.tileSize.x + 1, + this.tileSize.y, + this.tileSize.x + ); + case 180: + return new Rectangle( + this.origin.x, + this.origin.y - this.tileSize.y + 1, + this.tileSize.x, + this.tileSize.y + ); + case 270: + return new Rectangle( + this.origin.x, + this.origin.y, + this.tileSize.y, + this.tileSize.x + ); + default: + assert(false, "Invalid rotation"); + } } } @@ -111,7 +145,7 @@ export class StaticMapEntityComponent extends Component { * @returns {Vector} */ applyRotationToVector(vector) { - return vector.rotateFastMultipleOf90(this.rotation); + return vector.mirror(this.mirrored).rotateFastMultipleOf90(this.rotation); } /** @@ -120,7 +154,7 @@ export class StaticMapEntityComponent extends Component { * @returns {Vector} */ unapplyRotationToVector(vector) { - return vector.rotateFastMultipleOf90(360 - this.rotation); + return vector.rotateFastMultipleOf90(360 - this.rotation).mirror(this.mirrored); } /** @@ -129,7 +163,7 @@ export class StaticMapEntityComponent extends Component { * @returns {enumDirection} */ localDirectionToWorld(direction) { - return Vector.transformDirectionFromMultipleOf90(direction, this.rotation); + return Vector.transformDirectionFromMultipleOf90(Vector.mirrorDirection(direction, this.mirrored), this.rotation); } /** @@ -138,7 +172,7 @@ export class StaticMapEntityComponent extends Component { * @returns {enumDirection} */ worldDirectionToLocal(direction) { - return Vector.transformDirectionFromMultipleOf90(direction, 360 - this.rotation); + return Vector.mirrorDirection(Vector.transformDirectionFromMultipleOf90(direction, 360 - this.rotation), this.mirrored); } /** @@ -166,49 +200,13 @@ export class StaticMapEntityComponent extends Component { * @param {DrawParameters} parameters */ shouldBeDrawn(parameters) { - let x = 0; - let y = 0; - let w = 0; - let h = 0; - - switch (this.rotation) { - case 0: { - x = this.origin.x; - y = this.origin.y; - w = this.tileSize.x; - h = this.tileSize.y; - break; - } - case 90: { - x = this.origin.x - this.tileSize.y + 1; - y = this.origin.y; - w = this.tileSize.y; - h = this.tileSize.x; - break; - } - case 180: { - x = this.origin.x - this.tileSize.x + 1; - y = this.origin.y - this.tileSize.y + 1; - w = this.tileSize.x; - h = this.tileSize.y; - break; - } - case 270: { - x = this.origin.x; - y = this.origin.y - this.tileSize.x + 1; - w = this.tileSize.y; - h = this.tileSize.x; - break; - } - default: - assert(false, "Invalid rotation"); - } + const rect = this.getTileSpaceBounds(); return parameters.visibleRect.containsRect4Params( - x * globalConfig.tileSize, - y * globalConfig.tileSize, - w * globalConfig.tileSize, - h * globalConfig.tileSize + rect.x * globalConfig.tileSize, + rect.y * globalConfig.tileSize, + rect.w * globalConfig.tileSize, + rect.h * globalConfig.tileSize ); } @@ -238,7 +236,7 @@ export class StaticMapEntityComponent extends Component { worldY = overridePosition.y * globalConfig.tileSize; } - if (this.rotation === 0) { + if (this.rotation === 0 && this.mirrored == false) { // Early out, is faster sprite.drawCached( parameters, @@ -254,6 +252,9 @@ export class StaticMapEntityComponent extends Component { parameters.context.translate(rotationCenterX, rotationCenterY); parameters.context.rotate(Math_radians(this.rotation)); + if (this.mirrored == true) { + parameters.context.scale(-1.0, 1.0); + } sprite.drawCached( parameters, @@ -264,6 +265,9 @@ export class StaticMapEntityComponent extends Component { false ); + if (this.mirrored == true) { + parameters.context.scale(-1.0, 1.0); + } parameters.context.rotate(-Math_radians(this.rotation)); parameters.context.translate(-rotationCenterX, -rotationCenterY); } diff --git a/src/js/game/hud/parts/blueprint.js b/src/js/game/hud/parts/blueprint.js index c53163d9..cec505c2 100644 --- a/src/js/game/hud/parts/blueprint.js +++ b/src/js/game/hud/parts/blueprint.js @@ -8,6 +8,7 @@ import { findNiceIntegerValue } from "../../../core/utils"; import { Math_pow } from "../../../core/builtins"; import { blueprintShape } from "../../upgrades"; import { globalConfig } from "../../../core/config"; +import { enumItemProcessorTypes } from "../../components/item_processor"; const logger = createLogger("blueprint"); @@ -130,6 +131,36 @@ export class Blueprint { } } + /** + * Mirrors the blueprint horizontally + */ + mirror() { + for (let i = 0; i < this.entities.length; ++i) { + const entity = this.entities[i]; + const staticComp = entity.components.StaticMapEntity; + const beltComp = entity.components.Belt; + const itemProcComp = entity.components.ItemProcessor; + + staticComp.rotation = Vector.mirrorAngle(staticComp.rotation) + staticComp.originalRotation = Vector.mirrorAngle(staticComp.originalRotation) + staticComp.origin = staticComp.origin.mirror(); + + if (beltComp) { + // It is a belt! Flipping the direction is enough. + beltComp.direction = Vector.mirrorDirection( beltComp.direction ); + staticComp.blueprintSpriteKey = "sprites/blueprints/belt_" + beltComp.direction + ".png"; + } else if ( itemProcComp && ( + itemProcComp.type == enumItemProcessorTypes.rotater || + itemProcComp.type == enumItemProcessorTypes.rotaterCCW + ) ) { + // Don't flip the rotater + } else { + staticComp.mirrored = !staticComp.mirrored; + } + + } + } + /** * Checks if the blueprint can be placed at the given tile * @param {GameRoot} root diff --git a/src/js/game/hud/parts/blueprint_placer.js b/src/js/game/hud/parts/blueprint_placer.js index c98fbf2d..ca588502 100644 --- a/src/js/game/hud/parts/blueprint_placer.js +++ b/src/js/game/hud/parts/blueprint_placer.js @@ -37,6 +37,7 @@ export class HUDBlueprintPlacer extends BaseHUDPart { keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this); keyActionMapper.getBinding(KEYMAPPINGS.placement.pipette).add(this.abortPlacement, this); keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.rotateBlueprint, this); + keyActionMapper.getBinding(KEYMAPPINGS.placement.mirrorWhilePlacing).add(this.mirrorBlueprint, this); keyActionMapper.getBinding(KEYMAPPINGS.massSelect.pasteLastBlueprint).add(this.pasteBlueprint, this); this.root.camera.downPreHandler.add(this.onMouseDown, this); @@ -139,6 +140,20 @@ export class HUDBlueprintPlacer extends BaseHUDPart { } } + mirrorBlueprint() { + if (this.currentBlueprint.get()) { + if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) { + // Mirror vertically + this.currentBlueprint.get().rotateCw(); + this.currentBlueprint.get().mirror(); + this.currentBlueprint.get().rotateCcw(); + } else { + // Mirror horizontally + this.currentBlueprint.get().mirror(); + } + } + } + pasteBlueprint() { if (this.lastBlueprintUsed !== null) { this.root.hud.signals.pasteBlueprintRequested.dispatch(); diff --git a/src/js/game/hud/parts/building_placer.js b/src/js/game/hud/parts/building_placer.js index 0e9752ef..faa30ef2 100644 --- a/src/js/game/hud/parts/building_placer.js +++ b/src/js/game/hud/parts/building_placer.js @@ -229,11 +229,13 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { const { rotation, rotationVariant, + mirrored, connectedEntities, } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile( this.root, mouseTile, this.currentBaseRotation, + this.currentMirrored, this.currentVariant.get() ); @@ -272,6 +274,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { const staticComp = this.fakeEntity.components.StaticMapEntity; staticComp.origin = mouseTile; staticComp.rotation = rotation; + staticComp.mirrored = mirrored; staticComp.tileSize = metaBuilding.getDimensions(this.currentVariant.get()); metaBuilding.updateVariants(this.fakeEntity, rotationVariant, this.currentVariant.get()); @@ -280,6 +283,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { origin: mouseTile, rotation, rotationVariant, + mirrored, building: metaBuilding, variant: this.currentVariant.get(), }); diff --git a/src/js/game/hud/parts/building_placer_logic.js b/src/js/game/hud/parts/building_placer_logic.js index 337e43f7..55bce035 100644 --- a/src/js/game/hud/parts/building_placer_logic.js +++ b/src/js/game/hud/parts/building_placer_logic.js @@ -56,6 +56,12 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { */ this.preferredBaseRotations = {}; + /** + * The current mirror state + * @type {boolean} + */ + this.currentMirrored = false; + /** * Whether we are currently dragging * @type {boolean} @@ -103,6 +109,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { // KEYBINDINGS const keyActionMapper = this.root.keyMapper; keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.tryRotate, this); + keyActionMapper.getBinding(KEYMAPPINGS.placement.mirrorWhilePlacing).add(this.tryMirror, this); keyActionMapper.getBinding(KEYMAPPINGS.placement.cycleBuildingVariants).add(this.cycleVariants, this); keyActionMapper .getBinding(KEYMAPPINGS.placement.switchDirectionLockSide) @@ -265,6 +272,25 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { staticComp.rotation = this.currentBaseRotation; } } + /** + * Tries to mirror the current building + */ + tryMirror() { + const selectedBuilding = this.currentMetaBuilding.get(); + if (selectedBuilding) { + this.currentMirrored = !this.currentMirrored; + this.currentBaseRotation = Vector.mirrorAngle(this.currentBaseRotation); + const staticComp = this.fakeEntity.components.StaticMapEntity; + staticComp.mirrored = this.currentMirrored; + staticComp.rotation = this.currentBaseRotation; + + if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) { + // Rotate twice to get a vertical mirroring + this.tryRotate(); + this.tryRotate(); + } + } + } /** * Tries to delete the building under the mouse */ @@ -331,6 +357,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { this.currentMetaBuilding.set(extracted.metaBuilding); this.currentVariant.set(extracted.variant); this.currentBaseRotation = contents.components.StaticMapEntity.rotation; + this.currentMirrored = contents.components.StaticMapEntity.mirrored; // Make sure we selected something, and also make sure it's not a special entity // if (contents && !contents.components.Unremovable) { @@ -444,10 +471,11 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { } const metaBuilding = this.currentMetaBuilding.get(); - const { rotation, rotationVariant } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile( + const { rotation, rotationVariant, mirrored } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile( this.root, tile, this.currentBaseRotation, + this.currentMirrored, this.currentVariant.get() ); @@ -456,6 +484,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { rotation, rotationVariant, originalRotation: this.currentBaseRotation, + mirrored, building: this.currentMetaBuilding.get(), variant: this.currentVariant.get(), }); diff --git a/src/js/game/hud/parts/keybinding_overlay.js b/src/js/game/hud/parts/keybinding_overlay.js index 995fc5d9..f53c18f6 100644 --- a/src/js/game/hud/parts/keybinding_overlay.js +++ b/src/js/game/hud/parts/keybinding_overlay.js @@ -207,6 +207,13 @@ export class HUDKeybindingOverlay extends BaseHUDPart { condition: () => this.anyPlacementActive && !this.beltPlannerActive, }, + { + // Mirror + label: T.ingame.keybindingsOverlay.mirrorBuilding, + keys: [k.placement.mirrorWhilePlacing], + condition: () => this.anyPlacementActive && !this.beltPlannerActive, + }, + { // [BELT PLANNER] Flip Side label: T.ingame.keybindingsOverlay.plannerSwitchSide, diff --git a/src/js/game/key_action_mapper.js b/src/js/game/key_action_mapper.js index 45949ae3..30abd673 100644 --- a/src/js/game/key_action_mapper.js +++ b/src/js/game/key_action_mapper.js @@ -61,6 +61,7 @@ export const KEYMAPPINGS = { placement: { pipette: { keyCode: key("Q") }, rotateWhilePlacing: { keyCode: key("R") }, + mirrorWhilePlacing: { keyCode: key("N") }, rotateInverseModifier: { keyCode: 16 }, // SHIFT cycleBuildingVariants: { keyCode: key("T") }, cycleBuildings: { keyCode: 9 }, // TAB diff --git a/src/js/game/logic.js b/src/js/game/logic.js index 5c1bbf66..cd54b612 100644 --- a/src/js/game/logic.js +++ b/src/js/game/logic.js @@ -56,11 +56,12 @@ export class GameLogic { * @param {MetaBuilding} param0.building * @returns {boolean} */ - isAreaFreeToBuild({ origin, rotation, rotationVariant, variant, building }) { + isAreaFreeToBuild({ origin, rotation, mirrored, rotationVariant, variant, building }) { const checker = new StaticMapEntityComponent({ origin, tileSize: building.getDimensions(variant), rotation, + mirrored, blueprintSpriteKey: "", }); @@ -76,6 +77,7 @@ export class GameLogic { origin, building, rotation, + mirrored, rotationVariant, }) ) { @@ -95,10 +97,11 @@ export class GameLogic { * @param {Vector} param0.origin * @param {number} param0.rotation * @param {number} param0.rotationVariant + * @param {boolean} param0.mirrored * @param {MetaBuilding} param0.building * @returns {boolean} */ - checkCanReplaceBuilding({ original, origin, building, rotation, rotationVariant }) { + checkCanReplaceBuilding({ original, origin, building, rotation, mirrored, rotationVariant }) { if (!original.components.ReplaceableMapEntity) { // Can not get replaced at all return false; @@ -128,7 +131,7 @@ export class GameLogic { * @param {string} param0.variant * @param {MetaBuilding} param0.building */ - checkCanPlaceBuilding({ origin, rotation, rotationVariant, variant, building }) { + checkCanPlaceBuilding({ origin, rotation, mirrored, rotationVariant, variant, building }) { if (!building.getIsUnlocked(this.root)) { return false; } @@ -136,6 +139,7 @@ export class GameLogic { return this.isAreaFreeToBuild({ origin, rotation, + mirrored, rotationVariant, variant, building, @@ -153,13 +157,14 @@ export class GameLogic { * @param {MetaBuilding} param0.building * @returns {Entity} */ - tryPlaceBuilding({ origin, rotation, rotationVariant, originalRotation, variant, building }) { - if (this.checkCanPlaceBuilding({ origin, rotation, rotationVariant, variant, building })) { + tryPlaceBuilding({ origin, rotation, rotationVariant, originalRotation, mirrored, variant, building }) { + if (this.checkCanPlaceBuilding({ origin, rotation, mirrored, rotationVariant, variant, building })) { // Remove any removeable entities below const checker = new StaticMapEntityComponent({ origin, tileSize: building.getDimensions(variant), rotation, + mirrored, blueprintSpriteKey: "", }); @@ -182,6 +187,7 @@ export class GameLogic { origin, rotation, rotationVariant, + mirrored, originalRotation, variant, }); diff --git a/src/js/game/meta_building.js b/src/js/game/meta_building.js index 798e4d4c..bb82a86c 100644 --- a/src/js/game/meta_building.js +++ b/src/js/game/meta_building.js @@ -129,6 +129,15 @@ export class MetaBuilding { return true; } + /** + * Returns whether this building can be mirrored + * @param {string} variant + * @returns {boolean} + */ + isMirrorable(variant) { + return true; + } + /** * Returns whether this building is unlocked for the given game * @param {GameRoot} root @@ -151,15 +160,17 @@ export class MetaBuilding { * @param {Vector} param0.origin Origin tile * @param {number=} param0.rotation Rotation * @param {number} param0.originalRotation Original Rotation + * @param {boolean} param0.mirrored * @param {number} param0.rotationVariant Rotation variant * @param {string} param0.variant */ - createAndPlaceEntity({ root, origin, rotation, originalRotation, rotationVariant, variant }) { + createAndPlaceEntity({ root, origin, rotation, originalRotation, mirrored, rotationVariant, variant }) { const entity = this.createEntity({ root, origin, rotation, originalRotation, + mirrored, rotationVariant, variant, }); @@ -175,10 +186,11 @@ export class MetaBuilding { * @param {Vector} param0.origin Origin tile * @param {number=} param0.rotation Rotation * @param {number} param0.originalRotation Original Rotation + * @param {boolean} param0.mirrored * @param {number} param0.rotationVariant Rotation variant * @param {string} param0.variant */ - createEntity({ root, origin, rotation, originalRotation, rotationVariant, variant }) { + createEntity({ root, origin, rotation, originalRotation, mirrored, rotationVariant, variant }) { const entity = new Entity(root); const blueprintSprite = this.getBlueprintSprite(rotationVariant, variant); entity.addComponent( @@ -191,6 +203,7 @@ export class MetaBuilding { origin: new Vector(origin.x, origin.y), rotation, originalRotation, + mirrored, tileSize: this.getDimensions(variant).copy(), silhouetteColor: this.getSilhouetteColor(), blueprintSpriteKey: blueprintSprite ? blueprintSprite.spriteName : "", @@ -207,17 +220,18 @@ export class MetaBuilding { * @param {Vector} tile * @param {number} rotation * @param {string} variant - * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array }} + * @return {{ rotation: number, rotationVariant: number, mirrored: boolean, connectedEntities?: Array }} */ - computeOptimalDirectionAndRotationVariantAtTile(root, tile, rotation, variant) { + computeOptimalDirectionAndRotationVariantAtTile(root, tile, rotation, mirrored, variant) { if (!this.isRotateable(variant)) { - return { - rotation: 0, - rotationVariant: 0, - }; + rotation = 0; + } + if (!this.isMirrorable(variant)) { + mirrored = false; } return { rotation, + mirrored, rotationVariant: 0, }; } diff --git a/src/js/game/systems/belt.js b/src/js/game/systems/belt.js index 5e4ac7f0..70747e29 100644 --- a/src/js/game/systems/belt.js +++ b/src/js/game/systems/belt.js @@ -100,6 +100,7 @@ export class BeltSystem extends GameSystemWithFilter { this.root, new Vector(x, y), targetStaticComp.originalRotation, + false, defaultBuildingVariant ); targetStaticComp.rotation = rotation; diff --git a/src/js/game/systems/item_ejector.js b/src/js/game/systems/item_ejector.js index d6597ecd..e202b544 100644 --- a/src/js/game/systems/item_ejector.js +++ b/src/js/game/systems/item_ejector.js @@ -311,11 +311,9 @@ export class ItemEjectorSystem extends GameSystemWithFilter { continue; } - const realPosition = slot.pos.rotateFastMultipleOf90(staticComp.rotation); - const realDirection = Vector.transformDirectionFromMultipleOf90( - slot.direction, - staticComp.rotation - ); + const realPosition = staticComp.applyRotationToVector(slot.pos); + const realDirection = staticComp.localDirectionToWorld(slot.direction); + const realDirectionVector = enumDirectionToVector[realDirection]; const tileX = diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 930e5f29..8bb44150 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -276,6 +276,7 @@ ingame: selectBuildings: Select area stopPlacement: Stop placement rotateBuilding: Rotate building + mirrorBuilding: Mirror building placeMultiple: Place multiple reverseOrientation: Reverse orientation disableAutoOrientation: Disable auto orientation @@ -802,6 +803,7 @@ keybindings: pipette: Pipette rotateWhilePlacing: Rotate + mirrorWhilePlacing: Mirror rotateInverseModifier: >- Modifier: Rotate CCW instead cycleBuildingVariants: Cycle Variants