From 4a76135d575d250515781531077a7b9d00fcb3ec Mon Sep 17 00:00:00 2001 From: tobspr Date: Tue, 18 Jan 2022 20:38:20 +0100 Subject: [PATCH] Direction lock now indicates when there is a building inbetween --- mod_examples/buildings_have_cost.js | 4 +- mod_examples/custom_theme.js | 4 + src/js/game/blueprint.js | 6 +- src/js/game/hud/parts/building_placer.js | 138 ++++++++++++++++------- src/js/game/logic.js | 10 +- src/js/game/themes/dark.json | 4 + src/js/game/themes/light.json | 4 + 7 files changed, 119 insertions(+), 51 deletions(-) diff --git a/mod_examples/buildings_have_cost.js b/mod_examples/buildings_have_cost.js index b97197f9..79061d35 100644 --- a/mod_examples/buildings_have_cost.js +++ b/mod_examples/buildings_have_cost.js @@ -65,10 +65,10 @@ class Mod extends shapez.Mod { // Only allow placing an entity when there is enough currency this.modInterface.replaceMethod(shapez.GameLogic, "checkCanPlaceEntity", function ( $original, - [entity, offset] + [entity, options] ) { const storedCurrency = this.root.hubGoals.storedShapes[CURRENCY] || 0; - return storedCurrency > 0 && $original(entity, offset); + return storedCurrency > 0 && $original(entity, options); }); // Take shapes when placing a building diff --git a/mod_examples/custom_theme.js b/mod_examples/custom_theme.js index fd2e063a..a596799c 100644 --- a/mod_examples/custom_theme.js +++ b/mod_examples/custom_theme.js @@ -40,6 +40,10 @@ const RESOURCES = { color: "rgb(74, 237, 134)", background: "rgba(74, 237, 134, 0.2)", }, + error: { + color: "rgb(255, 137, 137)", + background: "rgba(255, 137, 137, 0.2)", + }, }, colorBlindPickerTile: "rgba(50, 50, 50, 0.4)", diff --git a/src/js/game/blueprint.js b/src/js/game/blueprint.js index 795b27c3..14848485 100644 --- a/src/js/game/blueprint.js +++ b/src/js/game/blueprint.js @@ -82,7 +82,7 @@ export class Blueprint { const rect = staticComp.getTileSpaceBounds(); rect.moveBy(tile.x, tile.y); - if (!parameters.root.logic.checkCanPlaceEntity(entity, tile)) { + if (!parameters.root.logic.checkCanPlaceEntity(entity, { offset: tile })) { parameters.context.globalAlpha = 0.3; } else { parameters.context.globalAlpha = 1; @@ -131,7 +131,7 @@ export class Blueprint { for (let i = 0; i < this.entities.length; ++i) { const entity = this.entities[i]; - if (root.logic.checkCanPlaceEntity(entity, tile)) { + if (root.logic.checkCanPlaceEntity(entity, { offset: tile })) { anyPlaceable = true; } } @@ -160,7 +160,7 @@ export class Blueprint { let count = 0; for (let i = 0; i < this.entities.length; ++i) { const entity = this.entities[i]; - if (!root.logic.checkCanPlaceEntity(entity, tile)) { + if (!root.logic.checkCanPlaceEntity(entity, { offset: tile })) { continue; } diff --git a/src/js/game/hud/parts/building_placer.js b/src/js/game/hud/parts/building_placer.js index ee7bc804..e4ecfe7e 100644 --- a/src/js/game/hud/parts/building_placer.js +++ b/src/js/game/hud/parts/building_placer.js @@ -61,7 +61,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { this.currentInterpolatedCornerTile = new Vector(); this.lockIndicatorSprites = {}; - layers.forEach(layer => { + [...layers, "error"].forEach(layer => { this.lockIndicatorSprites[layer] = this.makeLockIndicatorSprite(layer); }); @@ -76,7 +76,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { /** * Makes the lock indicator sprite for the given layer - * @param {Layer} layer + * @param {string} layer */ makeLockIndicatorSprite(layer) { const dims = 48; @@ -358,7 +358,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { rotationVariant ); - const canBuild = this.root.logic.checkCanPlaceEntity(this.fakeEntity); + const canBuild = this.root.logic.checkCanPlaceEntity(this.fakeEntity, {}); // Fade in / out parameters.context.lineWidth = 1; @@ -397,6 +397,42 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { } } + /** + * Checks if there are any entities in the way, returns true if there are + * @param {Vector} from + * @param {Vector} to + * @returns + */ + checkForObstales(from, to) { + assert(from.x === to.x || from.y === to.y, "Must be a straight line"); + + const prop = from.x === to.x ? "y" : "x"; + const current = from.copy(); + + const metaBuilding = this.currentMetaBuilding.get(); + this.fakeEntity.layer = metaBuilding.getLayer(); + const staticComp = this.fakeEntity.components.StaticMapEntity; + staticComp.origin = current; + staticComp.rotation = 0; + metaBuilding.updateVariants(this.fakeEntity, 0, this.currentVariant.get()); + staticComp.code = getCodeFromBuildingData( + this.currentMetaBuilding.get(), + this.currentVariant.get(), + 0 + ); + + const start = Math.min(from[prop], to[prop]); + const end = Math.max(from[prop], to[prop]); + + for (let i = start; i <= end; i++) { + current[prop] = i; + if (!this.root.logic.checkCanPlaceEntity(this.fakeEntity, { allowReplaceBuildings: false })) { + return true; + } + } + return false; + } + /** * @param {DrawParameters} parameters */ @@ -407,55 +443,73 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { return; } + const applyStyles = look => { + parameters.context.fillStyle = THEME.map.directionLock[look].color; + parameters.context.strokeStyle = THEME.map.directionLock[look].background; + parameters.context.lineWidth = 10; + }; + + if (!this.lastDragTile) { + // Not dragging yet + applyStyles(this.root.currentLayer); + const mouseWorld = this.root.camera.screenToWorld(mousePosition); + parameters.context.beginCircle(mouseWorld.x, mouseWorld.y, 4); + parameters.context.fill(); + return; + } + const mouseWorld = this.root.camera.screenToWorld(mousePosition); const mouseTile = mouseWorld.toTileSpace(); - parameters.context.fillStyle = THEME.map.directionLock[this.root.currentLayer].color; - parameters.context.strokeStyle = THEME.map.directionLock[this.root.currentLayer].background; - parameters.context.lineWidth = 10; + const startLine = this.lastDragTile.toWorldSpaceCenterOfTile(); + const endLine = mouseTile.toWorldSpaceCenterOfTile(); + const midLine = this.currentDirectionLockCorner.toWorldSpaceCenterOfTile(); + const anyObstacle = + this.checkForObstales(this.lastDragTile, this.currentDirectionLockCorner) || + this.checkForObstales(this.currentDirectionLockCorner, mouseTile); + + if (anyObstacle) { + applyStyles("error"); + } else { + applyStyles(this.root.currentLayer); + } parameters.context.beginCircle(mouseWorld.x, mouseWorld.y, 4); parameters.context.fill(); - if (this.lastDragTile) { - const startLine = this.lastDragTile.toWorldSpaceCenterOfTile(); - const endLine = mouseTile.toWorldSpaceCenterOfTile(); - const midLine = this.currentDirectionLockCorner.toWorldSpaceCenterOfTile(); + parameters.context.beginCircle(startLine.x, startLine.y, 8); + parameters.context.fill(); - parameters.context.beginCircle(startLine.x, startLine.y, 8); - parameters.context.fill(); + parameters.context.beginPath(); + parameters.context.moveTo(startLine.x, startLine.y); + parameters.context.lineTo(midLine.x, midLine.y); + parameters.context.lineTo(endLine.x, endLine.y); + parameters.context.stroke(); - parameters.context.beginPath(); - parameters.context.moveTo(startLine.x, startLine.y); - parameters.context.lineTo(midLine.x, midLine.y); - parameters.context.lineTo(endLine.x, endLine.y); - parameters.context.stroke(); + parameters.context.beginCircle(endLine.x, endLine.y, 5); + parameters.context.fill(); - parameters.context.beginCircle(endLine.x, endLine.y, 5); - parameters.context.fill(); + // Draw arrow + const arrowSprite = this.lockIndicatorSprites[anyObstacle ? "error" : this.root.currentLayer]; + const path = this.computeDirectionLockPath(); + for (let i = 0; i < path.length - 1; i += 1) { + const { rotation, tile } = path[i]; + const worldPos = tile.toWorldSpaceCenterOfTile(); + const angle = Math.radians(rotation); - // Draw arrow - const arrowSprite = this.lockIndicatorSprites[this.root.currentLayer]; - const path = this.computeDirectionLockPath(); - for (let i = 0; i < path.length - 1; i += 1) { - const { rotation, tile } = path[i]; - const worldPos = tile.toWorldSpaceCenterOfTile(); - const angle = Math.radians(rotation); - - parameters.context.translate(worldPos.x, worldPos.y); - parameters.context.rotate(angle); - parameters.context.drawImage( - arrowSprite, - -6, - -globalConfig.halfTileSize - - clamp((this.root.time.realtimeNow() * 1.5) % 1.0, 0, 1) * 1 * globalConfig.tileSize + - globalConfig.halfTileSize - - 6, - 12, - 12 - ); - parameters.context.rotate(-angle); - parameters.context.translate(-worldPos.x, -worldPos.y); - } + parameters.context.translate(worldPos.x, worldPos.y); + parameters.context.rotate(angle); + parameters.context.drawImage( + arrowSprite, + -6, + -globalConfig.halfTileSize - + clamp((this.root.time.realtimeNow() * 1.5) % 1.0, 0, 1) * 1 * globalConfig.tileSize + + globalConfig.halfTileSize - + 6, + 12, + 12 + ); + parameters.context.rotate(-angle); + parameters.context.translate(-worldPos.x, -worldPos.y); } } diff --git a/src/js/game/logic.js b/src/js/game/logic.js index 79104958..49bfb416 100644 --- a/src/js/game/logic.js +++ b/src/js/game/logic.js @@ -53,10 +53,12 @@ export class GameLogic { /** * Checks if the given entity can be placed * @param {Entity} entity - * @param {Vector=} offset Optional, move the entity by the given offset first + * @param {Object} param0 + * @param {boolean=} param0.allowReplaceBuildings + * @param {Vector=} param0.offset Optional, move the entity by the given offset first * @returns {boolean} true if the entity could be placed there */ - checkCanPlaceEntity(entity, offset = null) { + checkCanPlaceEntity(entity, { allowReplaceBuildings = false, offset = null }) { // Compute area of the building const rect = entity.components.StaticMapEntity.getTileSpaceBounds(); if (offset) { @@ -71,7 +73,7 @@ export class GameLogic { const otherEntity = this.root.map.getLayerContentXY(x, y, entity.layer); if (otherEntity) { const metaClass = otherEntity.components.StaticMapEntity.getMetaBuilding(); - if (!metaClass.getIsReplaceable()) { + if (!allowReplaceBuildings || !metaClass.getIsReplaceable()) { // This one is a direct blocker return false; } @@ -116,7 +118,7 @@ export class GameLogic { rotationVariant, variant, }); - if (this.checkCanPlaceEntity(entity)) { + if (this.checkCanPlaceEntity(entity, {})) { this.freeEntityAreaBeforeBuild(entity); this.root.map.placeStaticEntity(entity); this.root.entityMgr.registerEntity(entity); diff --git a/src/js/game/themes/dark.json b/src/js/game/themes/dark.json index 25571700..786cfda2 100644 --- a/src/js/game/themes/dark.json +++ b/src/js/game/themes/dark.json @@ -18,6 +18,10 @@ "wires": { "color": "rgb(74, 237, 134)", "background": "rgba(74, 237, 134, 0.2)" + }, + "error": { + "color": "rgb(255, 137, 137)", + "background": "rgba(255, 137, 137, 0.2)" } }, diff --git a/src/js/game/themes/light.json b/src/js/game/themes/light.json index 3bea6a80..1236d43d 100644 --- a/src/js/game/themes/light.json +++ b/src/js/game/themes/light.json @@ -18,6 +18,10 @@ "wires": { "color": "rgb(74, 237, 134)", "background": "rgba(74, 237, 134, 0.2)" + }, + "error": { + "color": "rgb(255, 137, 137)", + "background": "rgba(255, 137, 137, 0.2)" } },