diff --git a/res/ui/icons/info_button.png b/res/ui/icons/info_button.png new file mode 100644 index 00000000..abdf2af9 Binary files /dev/null and b/res/ui/icons/info_button.png differ diff --git a/res_raw/sounds/sfx/destroy_building.wav b/res_raw/sounds/sfx/destroy_building.wav new file mode 100644 index 00000000..ffb38158 --- /dev/null +++ b/res_raw/sounds/sfx/destroy_building.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1dc8775fdf5155097d6e1d60a436f48916af56eec14fb9034e71b32ad3b6f1b0 +size 358896 diff --git a/src/css/ingame_hud/color_blind_helper.scss b/src/css/ingame_hud/color_blind_helper.scss new file mode 100644 index 00000000..027b781b --- /dev/null +++ b/src/css/ingame_hud/color_blind_helper.scss @@ -0,0 +1,18 @@ +#ingame_HUD_ColorBlindBelowTileHelper { + position: absolute; + + @include SuperSmallText; + color: #fff; + background: $ingameHudBg; + @include S(padding, 5px); + @include S(top, 20px); + left: 50%; + transform: translateX(-50%); + text-transform: uppercase; + + &:not(.visible) { + display: none; + } + + @include DarkThemeInvert; +} diff --git a/src/css/ingame_hud/pinned_shapes.scss b/src/css/ingame_hud/pinned_shapes.scss index 68cf7e16..60e9159e 100644 --- a/src/css/ingame_hud/pinned_shapes.scss +++ b/src/css/ingame_hud/pinned_shapes.scss @@ -72,6 +72,24 @@ grid-row: 1 / 2; } + > .infoButton { + @include S(width, 8px); + @include S(height, 8px); + background: uiResource("icons/info_button.png") center center / 95% no-repeat; + position: absolute; + opacity: 0.7; + @include S(top, 13px); + @include S(left, -7px); + @include DarkThemeInvert; + @include IncreasedClickArea(2px); + transition: opacity 0.12s ease-in-out; + z-index: 100; + + &:hover { + opacity: 0.8; + } + } + &.goal, &.blueprint { .amountLabel::after { diff --git a/src/css/ingame_hud/shape_viewer.scss b/src/css/ingame_hud/shape_viewer.scss new file mode 100644 index 00000000..5d2484f0 --- /dev/null +++ b/src/css/ingame_hud/shape_viewer.scss @@ -0,0 +1,131 @@ +#ingame_HUD_ShapeViewer { + .dialogInner { + @include S(width, 160px); + } + .content { + display: flex; + flex-direction: column; + width: 100%; + align-items: center; + justify-items: center; + + .seperator { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + } + + .layer { + position: relative; + background: #eee; + + @include DarkThemeOverride { + background: rgba(0, 10, 20, 0.2); + } + @include S(width, 150px); + @include S(height, 100px); + display: flex; + align-items: center; + justify-content: center; + + > canvas { + @include S(width, 50px); + @include S(height, 50px); + } + + .quad { + position: absolute; + width: 50%; + height: 50%; + display: flex; + justify-content: center; + align-items: center; + box-sizing: border-box; + + $arrowDims: 23px; + $spacing: 9px; + @include S(padding, 6px); + + .colorLabel { + text-transform: uppercase; + @include SuperSmallText; + @include S(font-size, 9px); + } + + .emptyLabel { + text-transform: uppercase; + @include SuperSmallText; + @include S(font-size, 9px); + } + + &::after { + content: " "; + background: rgba(0, 10, 20, 0.5); + @include S(width, $arrowDims); + @include S(height, 1px); + position: absolute; + transform: rotate(45deg); + transform-origin: 50% 50%; + } + @include DarkThemeOverride { + &::after { + background: rgba(255, 255, 255, 0.5); + } + } + + &.quad-0 { + right: 0; + top: 0; + align-items: flex-start; + justify-content: flex-end; + + &::after { + @include S(left, $spacing); + @include S(bottom, $arrowDims / 2 + $spacing); + transform: rotate(-45deg); + } + } + &.quad-1 { + bottom: 0; + right: 0; + + align-items: flex-end; + justify-content: flex-end; + + &::after { + @include S(left, $spacing); + @include S(top, $arrowDims / 2 + $spacing); + transform: rotate(45deg); + } + } + &.quad-2 { + bottom: 0; + left: 0; + + align-items: flex-end; + justify-content: flex-start; + + &::after { + @include S(right, $spacing); + @include S(top, $arrowDims / 2 + $spacing); + transform: rotate(135deg); + } + } + &.quad-3 { + top: 0; + left: 0; + + align-items: flex-start; + justify-content: flex-start; + + &::after { + @include S(right, $spacing); + @include S(bottom, $arrowDims / 2 + $spacing); + transform: rotate(225deg); + } + } + } + } + } +} diff --git a/src/css/ingame_hud/shop.scss b/src/css/ingame_hud/shop.scss index 26f5d759..d8cc8a86 100644 --- a/src/css/ingame_hud/shop.scss +++ b/src/css/ingame_hud/shop.scss @@ -195,6 +195,25 @@ } } + button.showInfo { + @include S(width, 11px); + @include S(height, 11px); + background: uiResource("icons/info_button.png") center center / 95% no-repeat; + position: absolute; + @include S(top, 17px); + @include S(right, 2.5px); + opacity: 0.5; + cursor: pointer; + pointer-events: all; + @include IncreasedClickArea(5px); + transition: opacity 0.12s ease-in-out; + @include DarkThemeInvert; + + &:hover { + opacity: 0.6; + } + } + canvas { @include S(width, 40px); @include S(height, 40px); @@ -241,7 +260,7 @@ &.complete { background-color: $colorGreenBright; - + @include DarkThemeOverride { background-color: $colorGreenBright; } diff --git a/src/css/main.scss b/src/css/main.scss index 178f398b..6b758e80 100644 --- a/src/css/main.scss +++ b/src/css/main.scss @@ -49,6 +49,8 @@ @import "ingame_hud/blueprint_placer"; @import "ingame_hud/waypoints"; @import "ingame_hud/interactive_tutorial"; +@import "ingame_hud/color_blind_helper"; +@import "ingame_hud/shape_viewer"; // prettier-ignore $elements: @@ -74,6 +76,7 @@ ingame_HUD_buildings_toolbar, ingame_HUD_BlueprintPlacer, ingame_HUD_Waypoints_Hint, ingame_HUD_Watermark, +ingame_HUD_ColorBlindBelowTileHelper, // Overlays ingame_HUD_BetaOverlay, @@ -81,6 +84,7 @@ ingame_HUD_BetaOverlay, // Dialogs ingame_HUD_Shop, ingame_HUD_Statistics, +ingame_HUD_ShapeViewer, ingame_HUD_UnlockNotification, ingame_HUD_SettingsMenu, ingame_HUD_ModalDialogs; diff --git a/src/js/changelog.js b/src/js/changelog.js index 95553f19..b654f0de 100644 --- a/src/js/changelog.js +++ b/src/js/changelog.js @@ -3,6 +3,8 @@ export const CHANGELOG = [ version: "1.1.17", date: "unreleased", entries: [ + "Color blind mode! You can now activate it in the settings and it will show you which color is below your cursor (Either resource or on the belt)", + "Add info buttons to all shapes so you can figure out how they are built! (And also, which colors they have)", "Allow configuring autosave interval and disabling it in the settings", "The smart-tunnel placement has been reworked to properly replace belts. Thus the setting has been turned on again by default", "The soundtrack now has a higher quality on the standalone version than the web version", @@ -10,6 +12,8 @@ export const CHANGELOG = [ "Fix bug where belts in blueprints don't orient correctly (by hexy)", "Fix camera moving weird after dragging and holding (by hexy)", "Fix keybinding for pipette showing while pasting blueprints", + "Improve visibility of shape background in dark mode", + "Added sound when destroying a building", "Update tutorial image for tier 2 tunnels to explain mix/match (by jimmyshadow1)", "Prevent default actions on all keybindings in the web version so you don't accidentally use builtin browser shortcuts", ], diff --git a/src/js/game/hud/hud.js b/src/js/game/hud/hud.js index c322c707..efbde2ab 100644 --- a/src/js/game/hud/hud.js +++ b/src/js/game/hud/hud.js @@ -34,7 +34,8 @@ import { HUDPartTutorialHints } from "./parts/tutorial_hints"; import { HUDWaypoints } from "./parts/waypoints"; import { HUDInteractiveTutorial } from "./parts/interactive_tutorial"; import { HUDScreenshotExporter } from "./parts/screenshot_exporter"; -import { Entity } from "../entity"; +import { HUDColorBlindHelper } from "./parts/color_blind_helper"; +import { HUDShapeViewer } from "./parts/shape_viewer"; export class GameHUD { /** @@ -68,6 +69,7 @@ export class GameHUD { debugInfo: new HUDDebugInfo(this.root), dialogs: new HUDModalDialogs(this.root), screenshotExporter: new HUDScreenshotExporter(this.root), + shapeViewer: new HUDShapeViewer(this.root), }; this.signals = { @@ -76,7 +78,8 @@ export class GameHUD { shapeUnpinRequested: /** @type {TypedSignal<[string]>} */ (new Signal()), notification: /** @type {TypedSignal<[string, enumNotificationType]>} */ (new Signal()), buildingsSelectedForCopy: /** @type {TypedSignal<[Array]>} */ (new Signal()), - pasteBlueprintRequested: new Signal(), + pasteBlueprintRequested: /** @type {TypedSignal<[]>} */ (new Signal()), + viewShapeDetailsRequested: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()), }; if (!IS_MOBILE) { @@ -100,6 +103,10 @@ export class GameHUD { this.parts.vignetteOverlay = new HUDVignetteOverlay(this.root); } + if (this.root.app.settings.getAllSettings().enableColorBlindHelper) { + this.parts.colorBlindHelper = new HUDColorBlindHelper(this.root); + } + const frag = document.createDocumentFragment(); for (const key in this.parts) { this.parts[key].createElements(frag); @@ -208,7 +215,13 @@ export class GameHUD { * @param {DrawParameters} parameters */ draw(parameters) { - const partsOrder = ["waypoints", "massSelector", "buildingPlacer", "blueprintPlacer"]; + const partsOrder = [ + "waypoints", + "massSelector", + "buildingPlacer", + "blueprintPlacer", + "colorBlindHelper", + ]; for (let i = 0; i < partsOrder.length; ++i) { if (this.parts[partsOrder[i]]) { diff --git a/src/js/game/hud/parts/blueprint_placer.js b/src/js/game/hud/parts/blueprint_placer.js index ef46f101..c98fbf2d 100644 --- a/src/js/game/hud/parts/blueprint_placer.js +++ b/src/js/game/hud/parts/blueprint_placer.js @@ -10,6 +10,7 @@ import { blueprintShape } from "../../upgrades"; import { BaseHUDPart } from "../base_hud_part"; import { DynamicDomAttach } from "../dynamic_dom_attach"; import { Blueprint } from "./blueprint"; +import { SOUNDS } from "../../../platform/sound"; export class HUDBlueprintPlacer extends BaseHUDPart { createElements(parent) { @@ -103,7 +104,7 @@ export class HUDBlueprintPlacer extends BaseHUDPart { if (blueprint.tryPlace(this.root, tile)) { const cost = blueprint.getCost(); this.root.hubGoals.takeShapeByKey(blueprintShape, cost); - + this.root.soundProxy.playUi(SOUNDS.placeBuilding); // This actually feels weird // if (!this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeMultiple).pressed) { // this.currentBlueprint.set(null); diff --git a/src/js/game/hud/parts/building_placer_logic.js b/src/js/game/hud/parts/building_placer_logic.js index 0da81d4a..c9b3bfb8 100644 --- a/src/js/game/hud/parts/building_placer_logic.js +++ b/src/js/game/hud/parts/building_placer_logic.js @@ -10,6 +10,7 @@ import { Entity } from "../../entity"; import { KEYMAPPINGS } from "../../key_action_mapper"; import { defaultBuildingVariant, MetaBuilding } from "../../meta_building"; import { BaseHUDPart } from "../base_hud_part"; +import { SOUNDS } from "../../../platform/sound"; /** * Contains all logic for the building placer - this doesn't include the rendering @@ -215,6 +216,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { const contents = this.root.map.getTileContent(tile); if (contents) { this.root.logic.tryDeleteBuilding(contents); + this.root.soundProxy.playUi(SOUNDS.destroyBuilding); } } @@ -650,6 +652,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { const contents = this.root.map.getTileContentXY(x0, y0); if (contents && !contents.queuedForDestroy && !contents.destroyed) { this.root.logic.tryDeleteBuilding(contents); + this.root.soundProxy.playUi(SOUNDS.destroyBuilding); } } else { this.tryPlaceCurrentBuildingAt(new Vector(x0, y0)); diff --git a/src/js/game/hud/parts/color_blind_helper.js b/src/js/game/hud/parts/color_blind_helper.js new file mode 100644 index 00000000..4e6a0229 --- /dev/null +++ b/src/js/game/hud/parts/color_blind_helper.js @@ -0,0 +1,106 @@ +import { BaseHUDPart } from "../base_hud_part"; +import { makeDiv } from "../../../core/utils"; +import { TrackedState } from "../../../core/tracked_state"; +import { enumColors } from "../../colors"; +import { ColorItem } from "../../items/color_item"; +import { DrawParameters } from "../../../core/draw_parameters"; +import { THEME } from "../../theme"; +import { globalConfig } from "../../../core/config"; +import { T } from "../../../translations"; + +export class HUDColorBlindHelper extends BaseHUDPart { + createElements(parent) { + this.belowTileIndicator = makeDiv(parent, "ingame_HUD_ColorBlindBelowTileHelper", []); + } + + initialize() { + this.trackedColorBelowTile = new TrackedState(this.onColorBelowTileChanged, this); + } + + /** + * Called when the color below the current tile changed + * @param {enumColors|null} color + */ + onColorBelowTileChanged(color) { + this.belowTileIndicator.classList.toggle("visible", !!color); + if (color) { + this.belowTileIndicator.innerText = T.ingame.colors[color]; + } + } + + /** + * Computes the color below the current tile + * @returns {enumColors} + */ + computeColorBelowTile() { + const mousePosition = this.root.app.mousePosition; + if (!mousePosition) { + // Not on screen + return null; + } + + const worldPos = this.root.camera.screenToWorld(mousePosition); + const tile = worldPos.toTileSpace(); + const contents = this.root.map.getTileContent(tile); + + if (contents && !contents.components.Miner) { + const beltComp = contents.components.Belt; + + // Check if the belt has a color item + if (beltComp) { + const firstItem = beltComp.sortedItems[0]; + if (firstItem && firstItem[1] instanceof ColorItem) { + return firstItem[1].color; + } + } + + // Check if we are ejecting an item, if so use that color + const ejectorComp = contents.components.ItemEjector; + if (ejectorComp) { + for (let i = 0; i < ejectorComp.slots.length; ++i) { + const slot = ejectorComp.slots[i]; + if (slot.item && slot.item instanceof ColorItem) { + return slot.item.color; + } + } + } + } else { + // We hovered a lower layer, show the color there + const lowerLayer = this.root.map.getLowerLayerContentXY(tile.x, tile.y); + if (lowerLayer && lowerLayer instanceof ColorItem) { + return lowerLayer.color; + } + } + + return null; + } + + update() { + this.trackedColorBelowTile.set(this.computeColorBelowTile()); + } + + /** + * Draws the currently selected tile + * @param {DrawParameters} parameters + */ + draw(parameters) { + const mousePosition = this.root.app.mousePosition; + if (!mousePosition) { + // Not on screen + return null; + } + + const below = this.computeColorBelowTile(); + if (below) { + // We have something below our tile + const worldPos = this.root.camera.screenToWorld(mousePosition); + const tile = worldPos.toTileSpace().toWorldSpace(); + + parameters.context.strokeStyle = THEME.map.colorBlindPickerTile; + parameters.context.lineWidth = 1; + parameters.context.beginPath(); + parameters.context.rect(tile.x, tile.y, globalConfig.tileSize, globalConfig.tileSize); + parameters.context.stroke(); + } + } +} diff --git a/src/js/game/hud/parts/pinned_shapes.js b/src/js/game/hud/parts/pinned_shapes.js index 62afff25..3f935a0b 100644 --- a/src/js/game/hud/parts/pinned_shapes.js +++ b/src/js/game/hud/parts/pinned_shapes.js @@ -26,7 +26,8 @@ export class HUDPinnedShapes extends BaseHUDPart { * amountLabel: HTMLElement, * lastRenderedValue: string, * element: HTMLElement, - * detector?: ClickDetector + * detector?: ClickDetector, + * infoDetector?: ClickDetector * }>} */ this.handles = []; @@ -155,6 +156,10 @@ export class HUDPinnedShapes extends BaseHUDPart { if (detector) { detector.cleanup(); } + const infoDetector = this.handles[i].infoDetector; + if (infoDetector) { + infoDetector.cleanup(); + } } this.handles = []; @@ -198,12 +203,24 @@ export class HUDPinnedShapes extends BaseHUDPart { detector = new ClickDetector(element, { consumeEvents: true, preventDefault: true, + targetOnly: true, }); detector.click.add(() => this.unpinShape(key)); } else { element.classList.add("marked"); } + // Show small info icon + const infoButton = document.createElement("button"); + infoButton.classList.add("infoButton"); + element.appendChild(infoButton); + const infoDetector = new ClickDetector(infoButton, { + consumeEvents: true, + preventDefault: true, + targetOnly: true, + }); + infoDetector.click.add(() => this.root.hud.signals.viewShapeDetailsRequested.dispatch(definition)); + const amountLabel = makeDiv(element, null, ["amountLabel"], ""); const goal = this.findGoalValueForShape(key); @@ -216,6 +233,8 @@ export class HUDPinnedShapes extends BaseHUDPart { element, amountLabel, lastRenderedValue: "", + detector, + infoDetector, }); } diff --git a/src/js/game/hud/parts/shape_viewer.js b/src/js/game/hud/parts/shape_viewer.js new file mode 100644 index 00000000..fa083793 --- /dev/null +++ b/src/js/game/hud/parts/shape_viewer.js @@ -0,0 +1,109 @@ +import { BaseHUDPart } from "../base_hud_part"; +import { makeDiv, removeAllChildren } from "../../../core/utils"; +import { T } from "../../../translations"; +import { defaultBuildingVariant } from "../../meta_building"; +import { ShapeDefinition } from "../../shape_definition"; +import { KEYMAPPINGS, KeyActionMapper } from "../../key_action_mapper"; +import { InputReceiver } from "../../../core/input_receiver"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; + +export class HUDShapeViewer extends BaseHUDPart { + createElements(parent) { + this.background = makeDiv(parent, "ingame_HUD_ShapeViewer", ["ingameDialog"]); + + // DIALOG Inner / Wrapper + this.dialogInner = makeDiv(this.background, null, ["dialogInner"]); + this.title = makeDiv(this.dialogInner, null, ["title"], T.ingame.shapeViewer.title); + this.closeButton = makeDiv(this.title, null, ["closeButton"]); + this.trackClicks(this.closeButton, this.close); + this.contentDiv = makeDiv(this.dialogInner, null, ["content"]); + } + + initialize() { + this.root.hud.signals.viewShapeDetailsRequested.add(this.renderForShape, this); + + this.domAttach = new DynamicDomAttach(this.root, this.background, { + attachClass: "visible", + }); + + this.inputReciever = new InputReceiver("shape_viewer"); + this.keyActionMapper = new KeyActionMapper(this.root, this.inputReciever); + + this.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.close, this); + + this.close(); + } + + /** + * Closes the dialog + */ + close() { + this.visible = false; + document.body.classList.remove("ingameDialogOpen"); + this.root.app.inputMgr.makeSureDetached(this.inputReciever); + this.update(); + } + + /** + * Shows the viewer for a given definition + * @param {ShapeDefinition} definition + */ + renderForShape(definition) { + this.visible = true; + document.body.classList.add("ingameDialogOpen"); + this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever); + + removeAllChildren(this.contentDiv); + + const layers = definition.layers; + for (let i = 0; i < layers.length; ++i) { + const layerElem = makeDiv(this.contentDiv, null, ["layer", "layer-" + i]); + + let fakeLayers = []; + for (let k = 0; k < i; ++k) { + fakeLayers.push([null, null, null, null]); + } + fakeLayers.push(layers[i]); + + const thisLayerOnly = new ShapeDefinition({ layers: fakeLayers }); + const thisLayerCanvas = thisLayerOnly.generateAsCanvas(160); + layerElem.appendChild(thisLayerCanvas); + + for (let quad = 0; quad < 4; ++quad) { + const quadElem = makeDiv(layerElem, null, ["quad", "quad-" + quad]); + + const contents = layers[i][quad]; + if (contents) { + const colorLabelElem = makeDiv( + quadElem, + null, + ["colorLabel"], + T.ingame.colors[contents.color] + ); + } else { + const emptyLabelElem = makeDiv( + quadElem, + null, + ["emptyLabel"], + T.ingame.shapeViewer.empty + ); + } + } + + if (i < layers.length - 1) { + makeDiv(this.contentDiv, null, ["seperator"], "+"); + } + } + } + + /** + * Cleans up everything + */ + cleanup() { + document.body.classList.remove("ingameDialogOpen"); + } + + update() { + this.domAttach.update(this.visible); + } +} diff --git a/src/js/game/hud/parts/shop.js b/src/js/game/hud/parts/shop.js index 912fb3f1..a57b5d81 100644 --- a/src/js/game/hud/parts/shop.js +++ b/src/js/game/hud/parts/shop.js @@ -79,6 +79,7 @@ export class HUDShop extends BaseHUDPart { const requiredHandle = handle.requireIndexToElement[i]; requiredHandle.container.remove(); requiredHandle.pinDetector.cleanup(); + requiredHandle.infoDetector.cleanup(); } // Cleanup @@ -122,6 +123,10 @@ export class HUDShop extends BaseHUDPart { pinButton.classList.add("pin"); container.appendChild(pinButton); + const viewInfoButton = document.createElement("button"); + viewInfoButton.classList.add("showInfo"); + container.appendChild(viewInfoButton); + const currentGoalShape = this.root.hubGoals.currentGoal.definition.getHash(); if (shape === currentGoalShape) { pinButton.classList.add("isGoal"); @@ -145,6 +150,14 @@ export class HUDShop extends BaseHUDPart { } }); + const infoDetector = new ClickDetector(viewInfoButton, { + consumeEvents: true, + preventDefault: true, + }); + infoDetector.click.add(() => + this.root.hud.signals.viewShapeDetailsRequested.dispatch(shapeDef) + ); + handle.requireIndexToElement.push({ container, progressLabel, @@ -152,6 +165,7 @@ export class HUDShop extends BaseHUDPart { definition: shapeDef, required: amount, pinDetector, + infoDetector, }); }); } @@ -202,6 +216,7 @@ export class HUDShop extends BaseHUDPart { const requiredHandle = handle.requireIndexToElement[i]; requiredHandle.container.remove(); requiredHandle.pinDetector.cleanup(); + requiredHandle.infoDetector.cleanup(); } handle.requireIndexToElement = []; } diff --git a/src/js/game/shape_definition.js b/src/js/game/shape_definition.js index d785f6ab..8644e353 100644 --- a/src/js/game/shape_definition.js +++ b/src/js/game/shape_definition.js @@ -333,7 +333,7 @@ export class ShapeDefinition extends BasicSerializableObject { const quadrantSize = 10; const quadrantHalfSize = quadrantSize / 2; - context.fillStyle = "rgba(40, 50, 65, 0.1)"; + context.fillStyle = THEME.items.circleBackground; context.beginCircle(0, 0, quadrantSize * 1.15); context.fill(); diff --git a/src/js/game/sound_proxy.js b/src/js/game/sound_proxy.js index d0f4c660..0408586d 100644 --- a/src/js/game/sound_proxy.js +++ b/src/js/game/sound_proxy.js @@ -8,7 +8,7 @@ import { SOUNDS } from "../platform/sound"; const avgSoundDurationSeconds = 0.25; const maxOngoingSounds = 2; -const maxOngoingUiSounds = 25; +const maxOngoingUiSounds = 10; // Proxy to the application sound instance export class SoundProxy { diff --git a/src/js/game/themes/dark.json b/src/js/game/themes/dark.json index ef707663..a544b399 100644 --- a/src/js/game/themes/dark.json +++ b/src/js/game/themes/dark.json @@ -12,6 +12,8 @@ "directionLock": "rgb(74, 237, 134)", "directionLockTrack": "rgba(74, 237, 134, 0.2)", + "colorBlindPickerTile": "rgba(255, 255, 255, 0.5)", + "resources": { "shape": "#3d3f4a", "red": "#4a3d3f", @@ -26,6 +28,7 @@ "items": { "outline": "#111418", - "outlineWidth": 0.75 + "outlineWidth": 0.75, + "circleBackground": "rgba(20, 30, 40, 0.3)" } } diff --git a/src/js/game/themes/light.json b/src/js/game/themes/light.json index c5eead32..58231fa0 100644 --- a/src/js/game/themes/light.json +++ b/src/js/game/themes/light.json @@ -12,6 +12,8 @@ "directionLock": "rgb(74, 237, 134)", "directionLockTrack": "rgba(74, 237, 134, 0.2)", + "colorBlindPickerTile": "rgba(50, 50, 50, 0.4)", + "resources": { "shape": "#eaebec", "red": "#ffbfc1", @@ -27,6 +29,7 @@ "items": { "outline": "#55575a", - "outlineWidth": 0.75 + "outlineWidth": 0.75, + "circleBackground": "rgba(40, 50, 65, 0.1)" } } diff --git a/src/js/platform/sound.js b/src/js/platform/sound.js index dc6b073f..60d472f8 100644 --- a/src/js/platform/sound.js +++ b/src/js/platform/sound.js @@ -22,6 +22,7 @@ export const SOUNDS = { levelComplete: "level_complete", + destroyBuilding: "destroy_building", placeBuilding: "place_building", placeBelt: "place_belt", }; diff --git a/src/js/profile/application_settings.js b/src/js/profile/application_settings.js index 83aa21fb..16ec4cbd 100644 --- a/src/js/profile/application_settings.js +++ b/src/js/profile/application_settings.js @@ -172,6 +172,15 @@ export const allApplicationSettings = [ (app, value) => app.sound.setMusicMuted(value) ), + new BoolSetting( + "enableColorBlindHelper", + categoryApp, + /** + * @param {Application} app + */ + (app, value) => null + ), + // GAME new BoolSetting("offerHints", categoryGame, (app, value) => {}), @@ -269,6 +278,8 @@ class SettingsStorage { this.compactBuildingInfo = false; this.disableCutDeleteWarnings = false; + this.enableColorBlindHelper = false; + /** * @type {Object.} */ @@ -468,7 +479,7 @@ export class ApplicationSettings extends ReadWriteProxy { } getCurrentVersion() { - return 16; + return 17; } /** @param {{settings: SettingsStorage, version: number}} data */ @@ -536,6 +547,11 @@ export class ApplicationSettings extends ReadWriteProxy { data.version = 16; } + if (data.version < 17) { + data.settings.enableColorBlindHelper = false; + data.version = 17; + } + return ExplainedResult.good(); } } diff --git a/translations/base-en.yaml b/translations/base-en.yaml index a3d73ddf..d0a743ab 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -290,6 +290,17 @@ ingame: clearSelection: Clear Selection pipette: Pipette + # Names of the colors, used for the color blind mode + colors: + red: Red + green: Green + blue: Blue + yellow: Yellow + purple: Purple + cyan: Cyan + white: White + uncolored: No color + # Everything related to placing buildings (I.e. as soon as you selected a building # from the toolbar) buildingPlacement: @@ -384,6 +395,11 @@ ingame: description: Left-click a marker to jump to it, right-click to delete it.

Press to create a marker from the current view, or right-click to create a marker at the selected location. creationSuccessNotification: Marker has been created. + # Shape viewer + shapeViewer: + title: Layers + empty: Empty + # Interactive tutorial interactiveTutorial: title: Tutorial @@ -652,6 +668,11 @@ settings: description: >- Change the language. All translations are user contributed and might be incomplete! + enableColorBlindHelper: + title: Color Blind Mode + description: >- + Enables various tools which allow to play the game if you are color blind. + fullscreen: title: Fullscreen description: >-