diff --git a/src/js/game/colors.js b/src/js/game/colors.js index 9c7d0d2d..cb30b4b4 100644 --- a/src/js/game/colors.js +++ b/src/js/game/colors.js @@ -1,202 +1,213 @@ -/** @enum {string} */ -export const enumColors = { - red: "red", - green: "green", - blue: "blue", +/** @typedef {"red" | "green" | "blue" | "yellow" | "purple" | "cyan" | "white" | "uncolored" | "black"} Color **/ +/** @typedef {"r" | "g" | "b" | "y" | "p" | "c" | "w" | "u" | "0"} ShortColor **/ +/** @typedef {"#ff666a" | "#78ff66" | "#66a7ff" | "#fcf52a" | "#dd66ff" | "#00fcff" | "#ffffff" | "#aaaaaa" | "#31383a" } HexColor - yellow: "yellow", - purple: "purple", - cyan: "cyan", + /** @type {Color[]} **/ +export const colors = ["red", "green", "blue", "yellow", "purple", "cyan", "white", "uncolored", "black"]; - white: "white", +/** @type {ShortColor[]} **/ +export const shortColors = ["r", "g", "b", "y", "p", "c", "w", "u", "0"]; + +/** @type {Record} **/ +export const colorShortColorMap = { + red: "r", + green: "g", + blue: "b", + yellow: "y", + purple: "p", + cyan: "c", + white: "w", + uncolored: "u", + black: "0", +}; + +/** @type {Record} **/ +export const shortColorColorMap = { + "r": "red", + "g": "green", + "b": "blue", + "y": "yellow", + "p": "purple", + "c": "cyan", + "w": "white", + "u": "uncolored", + "0": "black", +}; + +/** @type {Record} **/ +export const colorHexColorMap = { + red: "#ff666a", + green: "#78ff66", + blue: "#66a7ff", + yellow: "#fcf52a", + purple: "#dd66ff", + cyan: "#00fcff", + white: "#ffffff", + uncolored: "#aaaaaa", + black: "#31383a", +}; + +/** @type {Record} **/ +export const colorInvertedMap = { + red: "cyan", + green: "purple", + blue: "yellow", + yellow: "blue", + purple: "green", + cyan: "red", + white: "black", uncolored: "uncolored", - - black: "black", + black: "white", }; -/** @enum {string} */ -export const enumColorToShortcode = { - [enumColors.red]: "r", - [enumColors.green]: "g", - [enumColors.blue]: "b", - - [enumColors.yellow]: "y", - [enumColors.purple]: "p", - [enumColors.cyan]: "c", - - [enumColors.white]: "w", - [enumColors.uncolored]: "u", - - [enumColors.black]: "0", -}; - -/** @enum {enumColors} */ -export const enumShortcodeToColor = {}; -for (const key in enumColorToShortcode) { - enumShortcodeToColor[enumColorToShortcode[key]] = key; -} - -/** @enum {string} */ -export const enumColorsToHexCode = { - [enumColors.red]: "#ff666a", - [enumColors.green]: "#78ff66", - [enumColors.blue]: "#66a7ff", - - // red + green - [enumColors.yellow]: "#fcf52a", - - // red + blue - [enumColors.purple]: "#dd66ff", - - // blue + green - [enumColors.cyan]: "#00fcff", - - // blue + green + red - [enumColors.white]: "#ffffff", - - [enumColors.black]: "#31383a", - - [enumColors.uncolored]: "#aaaaaa", -}; - -/** @enum {enumColors} */ -export const enumInvertedColors = { - [enumColors.red]: enumColors.cyan, - [enumColors.green]: enumColors.purple, - [enumColors.blue]: enumColors.yellow, - - [enumColors.yellow]: enumColors.blue, - [enumColors.purple]: enumColors.green, - [enumColors.cyan]: enumColors.red, - - [enumColors.white]: enumColors.black, - [enumColors.black]: enumColors.white, - - [enumColors.uncolored]: enumColors.uncolored, -}; - -const c = enumColors; -/** @enum {Object.} */ -export const enumColorMixingResults = { +/** @type {Record>} **/ +export const colorMixingMap = { // 255, 0, 0 - [c.red]: { - [c.green]: c.yellow, - [c.blue]: c.purple, - - [c.yellow]: c.yellow, - [c.purple]: c.purple, - [c.cyan]: c.white, - - [c.white]: c.white, - [c.black]: c.red, + red: { + red: "red", + green: "yellow", + blue: "purple", + yellow: "yellow", + purple: "purple", + cyan: "white", + white: "white", + uncolored: "red", + black: "red", }, // 0, 255, 0 - [c.green]: { - [c.blue]: c.cyan, - - [c.yellow]: c.yellow, - [c.purple]: c.white, - [c.cyan]: c.cyan, - - [c.white]: c.white, - [c.black]: c.green, + green: { + red: "yellow", + blue: "cyan", + green: "green", + yellow: "yellow", + purple: "white", + cyan: "cyan", + white: "white", + uncolored: "green", + black: "green", }, // 0, 255, 0 - [c.blue]: { - [c.yellow]: c.white, - [c.purple]: c.purple, - [c.cyan]: c.cyan, - - [c.white]: c.white, - [c.black]: c.blue, + blue: { + red: "purple", + green: "cyan", + blue: "blue", + yellow: "white", + purple: "purple", + cyan: "cyan", + white: "white", + uncolored: "blue", + black: "blue", }, // 255, 255, 0 - [c.yellow]: { - [c.purple]: c.white, - [c.cyan]: c.white, - [c.black]: c.yellow, + yellow: { + red: "yellow", + green: "yellow", + blue: "white", + purple: "white", + cyan: "white", + yellow: "yellow", + white: "white", + uncolored: "yellow", + black: "yellow", }, // 255, 0, 255 - [c.purple]: { - [c.cyan]: c.white, - [c.black]: c.purple, + purple: { + red: "purple", + green: "white", + blue: "purple", + cyan: "white", + yellow: "white", + purple: "purple", + white: "white", + uncolored: "purple", + black: "purple", }, // 0, 255, 255 - [c.cyan]: {}, + cyan: { + red: "white", + green: "cyan", + blue: "cyan", + cyan: "cyan", + yellow: "white", + purple: "white", + white: "white", + uncolored: "cyan", + black: "cyan", + }, //// SPECIAL COLORS // 255, 255, 255 - [c.white]: { - // auto + white: { + red: "white", + green: "white", + blue: "white", + cyan: "white", + yellow: "white", + purple: "white", + white: "white", + uncolored: "white", + black: "uncolored", }, // X, X, X - [c.uncolored]: { - // auto + uncolored: { + red: "red", + green: "green", + blue: "blue", + cyan: "cyan", + yellow: "yellow", + purple: "purple", + white: "white", + uncolored: "uncolored", + black: "black", }, - [c.black]: { - // auto - [c.white]: c.uncolored, - [c.cyan]: c.cyan, - [c.uncolored]: c.uncolored, + black: { + red: "red", + green: "green", + blue: "blue", + cyan: "cyan", + yellow: "yellow", + purple: "purple", + white: "uncolored", + uncolored: "black", + black: "black", }, }; -// Create same color lookups -for (const color in enumColors) { - enumColorMixingResults[color][color] = color; - - // Anything with white is white again, except for black which creates gray - if (color !== enumColors.black) { - enumColorMixingResults[color][c.white] = c.white; - } - - // Anything with uncolored is the same color - enumColorMixingResults[color][c.uncolored] = color; -} - -// Create reverse lookup and check color mixing lookups -for (const colorA in enumColorMixingResults) { - for (const colorB in enumColorMixingResults[colorA]) { - const resultColor = enumColorMixingResults[colorA][colorB]; - if (!enumColorMixingResults[colorB]) { - enumColorMixingResults[colorB] = { - [colorA]: resultColor, - }; - } else { - const existingResult = enumColorMixingResults[colorB][colorA]; - if (existingResult && existingResult !== resultColor) { - assertAlways( - false, - "invalid color mixing configuration, " + - colorA + - " + " + - colorB + - " is " + - resultColor + - " but " + - colorB + - " + " + - colorA + - " is " + - existingResult - ); - } - enumColorMixingResults[colorB][colorA] = resultColor; +for (const colorA in colorMixingMap) { + for (const colorB in colorMixingMap[colorA]) { + const resultColor = colorMixingMap[colorA][colorB]; + const existingResult = colorMixingMap[colorB][colorA]; + if (existingResult && existingResult !== resultColor) { + assertAlways( + false, + "invalid color mixing configuration, " + + colorA + + " + " + + colorB + + " is " + + resultColor + + " but " + + colorB + + " + " + + colorA + + " is " + + existingResult + ); } } } -for (const colorA in enumColorMixingResults) { - for (const colorB in enumColorMixingResults) { - if (!enumColorMixingResults[colorA][colorB]) { +for (const colorA in colorMixingMap) { + for (const colorB in colorMixingMap) { + if (!colorMixingMap[colorA][colorB]) { assertAlways(false, "Color mixing of", colorA, "with", colorB, "is not defined"); } } diff --git a/src/js/game/hub_goals.js b/src/js/game/hub_goals.js index 36f8f107..229af3be 100644 --- a/src/js/game/hub_goals.js +++ b/src/js/game/hub_goals.js @@ -2,7 +2,7 @@ import { globalConfig } from "../core/config"; import { queryParamOptions } from "../core/query_parameters"; import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils"; import { BasicSerializableObject, types } from "../savegame/serialization"; -import { enumColors } from "./colors"; +import { colors } from "./colors"; import { enumItemProcessorTypes } from "./components/item_processor"; import { GameRoot, enumLayer } from "./root"; import { enumSubShape, ShapeDefinition } from "./shape_definition"; @@ -328,8 +328,7 @@ export class HubGoals extends BasicSerializableObject { /** @type {Array} */ let layers = []; - // @ts-ignore - const randomColor = () => randomChoice(Object.values(enumColors)); + const randomColor = () => randomChoice(colors); // @ts-ignore const randomShape = () => randomChoice(Object.values(enumSubShape)); diff --git a/src/js/game/hud/parts/color_blind_helper.js b/src/js/game/hud/parts/color_blind_helper.js index c7dd6288..92edb2f2 100644 --- a/src/js/game/hud/parts/color_blind_helper.js +++ b/src/js/game/hud/parts/color_blind_helper.js @@ -1,7 +1,6 @@ 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"; @@ -10,6 +9,10 @@ import { T } from "../../../translations"; import { enumItemType } from "../../base_item"; import { enumLayer } from "../../root"; +/** + * @typedef {import("../../colors").Color} Color + */ + export class HUDColorBlindHelper extends BaseHUDPart { createElements(parent) { this.belowTileIndicator = makeDiv(parent, "ingame_HUD_ColorBlindBelowTileHelper", []); @@ -21,7 +24,7 @@ export class HUDColorBlindHelper extends BaseHUDPart { /** * Called when the color below the current tile changed - * @param {enumColors|null} color + * @param {Color|null} color */ onColorBelowTileChanged(color) { this.belowTileIndicator.classList.toggle("visible", !!color); @@ -32,7 +35,7 @@ export class HUDColorBlindHelper extends BaseHUDPart { /** * Computes the color below the current tile - * @returns {enumColors} + * @returns {Color} */ computeColorBelowTile() { const mousePosition = this.root.app.mousePosition; diff --git a/src/js/game/items/color_item.js b/src/js/game/items/color_item.js index d1b9a217..79017108 100644 --- a/src/js/game/items/color_item.js +++ b/src/js/game/items/color_item.js @@ -3,16 +3,21 @@ import { smoothenDpi } from "../../core/dpi_manager"; import { DrawParameters } from "../../core/draw_parameters"; import { types } from "../../savegame/serialization"; import { BaseItem, enumItemType } from "../base_item"; -import { enumColors, enumColorsToHexCode } from "../colors"; +import { colors, colorHexColorMap } from "../colors"; import { THEME } from "../theme"; +/** + * @typedef {import("../colors").Color} Color + * @typedef {import("../colors").HexColor} HexColor + */ + export class ColorItem extends BaseItem { static getId() { return "color"; } static getSchema() { - return types.enum(enumColors); + return types.enum(colors); } serialize() { @@ -28,7 +33,7 @@ export class ColorItem extends BaseItem { } /** - * @param {enumColors} color + * @param {Color} color */ constructor(color) { super(); @@ -36,6 +41,9 @@ export class ColorItem extends BaseItem { this.bufferGenerator = null; } + /** + * @returns {HexColor} + */ getBackgroundColorAsResource() { return THEME.map.resources[this.color]; } @@ -76,7 +84,7 @@ export class ColorItem extends BaseItem { context.translate((w * dpi) / 2, (h * dpi) / 2); context.scale((dpi * w) / 12, (dpi * h) / 12); - context.fillStyle = enumColorsToHexCode[this.color]; + context.fillStyle = colorHexColorMap[this.color]; context.strokeStyle = THEME.items.outline; context.lineWidth = 2 * THEME.items.outlineWidth; context.beginCircle(2, -1, 3); diff --git a/src/js/game/map_chunk.js b/src/js/game/map_chunk.js index dd9ba81d..1538dbbc 100644 --- a/src/js/game/map_chunk.js +++ b/src/js/game/map_chunk.js @@ -4,7 +4,6 @@ import { createLogger } from "../core/logging"; import { clamp, fastArrayDeleteValueIfContained, make2DUndefinedArray } from "../core/utils"; import { Vector } from "../core/vector"; import { BaseItem } from "./base_item"; -import { enumColors } from "./colors"; import { Entity } from "./entity"; import { ColorItem } from "./items/color_item"; import { ShapeItem } from "./items/shape_item"; @@ -13,6 +12,8 @@ import { RandomNumberGenerator } from "../core/rng"; const logger = createLogger("map_chunk"); +/** @typedef {import("./colors").Color} Color **/ + export class MapChunk { /** * @@ -139,9 +140,10 @@ export class MapChunk { */ internalGenerateColorPatch(rng, colorPatchSize, distanceToOriginInChunks) { // First, determine available colors - let availableColors = [enumColors.red, enumColors.green]; + /** @type {Color[]} */ + const availableColors = ["red", "green"]; if (distanceToOriginInChunks > 2) { - availableColors.push(enumColors.blue); + availableColors.push("blue"); } this.internalGeneratePatch(rng, colorPatchSize, new ColorItem(rng.choice(availableColors))); } @@ -268,7 +270,7 @@ export class MapChunk { */ generatePredefined(rng) { if (this.x === 0 && this.y === 0) { - this.internalGeneratePatch(rng, 2, new ColorItem(enumColors.red), 7, 7); + this.internalGeneratePatch(rng, 2, new ColorItem("red"), 7, 7); return true; } if (this.x === -1 && this.y === 0) { @@ -283,7 +285,7 @@ export class MapChunk { } if (this.x === -1 && this.y === -1) { - this.internalGeneratePatch(rng, 2, new ColorItem(enumColors.green)); + this.internalGeneratePatch(rng, 2, new ColorItem("green")); return true; } diff --git a/src/js/game/shape_definition.js b/src/js/game/shape_definition.js index f6117a52..935d332e 100644 --- a/src/js/game/shape_definition.js +++ b/src/js/game/shape_definition.js @@ -5,13 +5,7 @@ import { DrawParameters } from "../core/draw_parameters"; import { createLogger } from "../core/logging"; import { Vector } from "../core/vector"; import { BasicSerializableObject, types } from "../savegame/serialization"; -import { - enumColors, - enumColorsToHexCode, - enumColorToShortcode, - enumShortcodeToColor, - enumInvertedColors, -} from "./colors"; +import { colorHexColorMap, colorInvertedMap, colorShortColorMap, shortColorColorMap } from "./colors"; import { THEME } from "./theme"; const rusha = require("rusha"); @@ -19,13 +13,9 @@ const rusha = require("rusha"); const logger = createLogger("shape_definition"); /** - * @typedef {{ - * subShape: enumSubShape, - * color: enumColors, - * }} ShapeLayerItem - */ - -/** + * @typedef {import("./colors").Color} Color + * @typedef {{ subShape: enumSubShape, color: Color }} ShapeLayerItem + * * Order is Q1 (tr), Q2(br), Q3(bl), Q4(tl) * @typedef {[ShapeLayerItem?, ShapeLayerItem?, ShapeLayerItem?, ShapeLayerItem?]} ShapeLayer */ @@ -62,13 +52,13 @@ for (const key in enumSubShapeToShortcode) { /** * Converts the given parameters to a valid shape definition * @param {*} layers - * @returns {Array} + * @returns {Array} */ export function createSimpleShape(layers) { layers.forEach(layer => { layer.forEach(item => { if (item) { - item.color = item.color || enumColors.uncolored; + item.color = item.color || "uncolored"; } }); }); @@ -140,7 +130,7 @@ export class ShapeDefinition extends BasicSerializableObject { for (let quad = 0; quad < 4; ++quad) { const shapeText = text[quad * 2 + 0]; const subShape = enumShortcodeToSubShape[shapeText]; - const color = enumShortcodeToColor[text[quad * 2 + 1]]; + const color = shortColorColorMap[text[quad * 2 + 1]]; if (subShape) { assert(color, "Invalid shape short key:", key); quads[quad] = { @@ -197,7 +187,7 @@ export class ShapeDefinition extends BasicSerializableObject { const shapeText = text[quad * 2 + 0]; const colorText = text[quad * 2 + 1]; const subShape = enumShortcodeToSubShape[shapeText]; - const color = enumShortcodeToColor[colorText]; + const color = shortColorColorMap[colorText]; // Valid shape if (subShape) { @@ -267,7 +257,7 @@ export class ShapeDefinition extends BasicSerializableObject { for (let quadrant = 0; quadrant < layer.length; ++quadrant) { const item = layer[quadrant]; if (item) { - id += enumSubShapeToShortcode[item.subShape] + enumColorToShortcode[item.color]; + id += enumSubShapeToShortcode[item.subShape] + colorShortColorMap[item.color]; } else { id += "--"; } @@ -362,7 +352,7 @@ export class ShapeDefinition extends BasicSerializableObject { context.translate(centerQuadrantX, centerQuadrantY); context.rotate(rotation); - context.fillStyle = enumColorsToHexCode[color]; + context.fillStyle = colorHexColorMap[color]; context.strokeStyle = THEME.items.outline; context.lineWidth = THEME.items.outlineWidth; @@ -581,7 +571,7 @@ export class ShapeDefinition extends BasicSerializableObject { /** * Clones the shape and colors everything in the given color - * @param {enumColors} color + * @param {Color} color */ cloneAndPaintWith(color) { const newLayers = this.internalCloneLayers(); @@ -608,7 +598,7 @@ export class ShapeDefinition extends BasicSerializableObject { for (let quadrantIndex = 0; quadrantIndex < 4; ++quadrantIndex) { const item = quadrants[quadrantIndex]; if (item) { - item.color = enumInvertedColors[item.color]; + item.color = colorInvertedMap[item.color]; } } } @@ -617,7 +607,7 @@ export class ShapeDefinition extends BasicSerializableObject { /** * Clones the shape and colors everything in the given colors - * @param {[enumColors, enumColors, enumColors, enumColors]} colors + * @param {[Color, Color, Color, Color]} colors */ cloneAndPaintWith4Colors(colors) { const newLayers = this.internalCloneLayers(); diff --git a/src/js/game/shape_definition_manager.js b/src/js/game/shape_definition_manager.js index a3b8c841..72443390 100644 --- a/src/js/game/shape_definition_manager.js +++ b/src/js/game/shape_definition_manager.js @@ -2,10 +2,14 @@ import { BasicSerializableObject } from "../savegame/serialization"; import { GameRoot } from "./root"; import { ShapeDefinition, enumSubShape } from "./shape_definition"; import { createLogger } from "../core/logging"; -import { enumColors } from "./colors"; const logger = createLogger("shape_definition_manager"); +/** + * @typedef {import("./colors").Color} Color + * @typedef {import("./shape_definition").ShapeLayer} ShapeLayer + */ + export class ShapeDefinitionManager extends BasicSerializableObject { static getId() { return "ShapeDefinitionManager"; @@ -165,7 +169,7 @@ export class ShapeDefinitionManager extends BasicSerializableObject { /** * Generates a definition for painting it with the given color * @param {ShapeDefinition} definition - * @param {enumColors} color + * @param {Color} color * @returns {ShapeDefinition} */ shapeActionPaintWith(definition, color) { @@ -198,7 +202,7 @@ export class ShapeDefinitionManager extends BasicSerializableObject { /** * Generates a definition for painting it with the 4 colors * @param {ShapeDefinition} definition - * @param {[enumColors, enumColors, enumColors, enumColors]} colors + * @param {[Color, Color, Color, Color]} colors * @returns {ShapeDefinition} */ shapeActionPaintWith4Colors(definition, colors) { @@ -232,10 +236,8 @@ export class ShapeDefinitionManager extends BasicSerializableObject { * @param {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} subShapes * @returns {ShapeDefinition} */ - getDefinitionFromSimpleShapes(subShapes, color = enumColors.uncolored) { - const shapeLayer = /** @type {import("./shape_definition").ShapeLayer} */ (subShapes.map( - subShape => ({ subShape, color }) - )); + getDefinitionFromSimpleShapes(subShapes, color = "uncolored") { + const shapeLayer = /** @type {ShapeLayer} */ (subShapes.map(subShape => ({ subShape, color }))); return this.registerOrReturnHandle(new ShapeDefinition({ layers: [shapeLayer] })); } diff --git a/src/js/game/systems/item_processor.js b/src/js/game/systems/item_processor.js index 5bebfed7..f19ed585 100644 --- a/src/js/game/systems/item_processor.js +++ b/src/js/game/systems/item_processor.js @@ -1,6 +1,6 @@ import { globalConfig } from "../../core/config"; import { BaseItem, enumItemType } from "../base_item"; -import { enumColorMixingResults, enumInvertedColors } from "../colors"; +import { colorInvertedMap, colorMixingMap } from "../colors"; import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; import { Entity } from "../entity"; import { GameSystemWithFilter } from "../game_system_with_filter"; @@ -249,14 +249,10 @@ export class ItemProcessorSystem extends GameSystemWithFilter { const color1 = item1.color; const color2 = item2.color; - // Try finding mixer color, and if we can't mix it we simply return the same color - const mixedColor = enumColorMixingResults[color1][color2]; - let resultColor = color1; - if (mixedColor) { - resultColor = mixedColor; - } + const mixedColor = colorMixingMap[color1][color2]; + outItems.push({ - item: new ColorItem(resultColor), + item: new ColorItem(mixedColor), }); break; @@ -361,7 +357,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter { if (item.getItemType() === enumItemType.color) { const colorItem = /** @type {ColorItem} */ (items[0].item); - const newColor = enumInvertedColors[colorItem.color]; + const newColor = colorInvertedMap[colorItem.color]; outItems.push({ item: new ColorItem(newColor), requiredSlot: 0,