1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-12-16 03:31:52 +00:00

Rewrite color creation model

This commit is contained in:
Dimava 2020-06-08 13:46:37 +03:00
parent e3f05aae45
commit ef43022f19
4 changed files with 255 additions and 123 deletions

View File

@ -1,3 +1,5 @@
import { customColors } from "./custom/colors";
/** @enum {string} */ /** @enum {string} */
export const enumColors = { export const enumColors = {
red: "red", red: "red",
@ -5,7 +7,7 @@ export const enumColors = {
blue: "blue", blue: "blue",
yellow: "yellow", yellow: "yellow",
purple: "purple", magenta: "magenta",
cyan: "cyan", cyan: "cyan",
white: "white", white: "white",
@ -19,7 +21,7 @@ export const enumColorToShortcode = {
[enumColors.blue]: "b", [enumColors.blue]: "b",
[enumColors.yellow]: "y", [enumColors.yellow]: "y",
[enumColors.purple]: "p", [enumColors.magenta]: "p",
[enumColors.cyan]: "c", [enumColors.cyan]: "c",
[enumColors.white]: "w", [enumColors.white]: "w",
@ -28,140 +30,234 @@ export const enumColorToShortcode = {
/** @enum {enumColors} */ /** @enum {enumColors} */
export const enumShortcodeToColor = {}; export const enumShortcodeToColor = {};
for (const key in enumColorToShortcode) {
enumShortcodeToColor[enumColorToShortcode[key]] = key;
}
/** @enum {string} */ /** @enum {string} */
export const enumColorsToHexCode = { 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.uncolored]: "#aaaaaa",
};
const c = enumColors; const c = enumColors;
/** @enum {Object.<string, string>} */ /** @enum {Object.<string, Object>} */
export const enumColorMixingResults = { export const enumColorMixingResults = {};
// 255, 0, 0
[c.red]: {
[c.green]: c.yellow,
[c.blue]: c.purple,
[c.yellow]: c.yellow, /**
[c.purple]: c.purple, * @typedef {Object} ColorData
[c.cyan]: c.white, * @property {string} id
* @property {string} code
* @property {string} hex
* @property {string[][] | string[]} [mixingFrom]
* @property {Object.<string, string>} [mixing]
* @property {boolean} [spawnable]
* @property {number} [minDistance]
*/
[c.white]: c.white, /** @enum {ColorData} */
export const allColorData = {
uncolored: {
id: "uncolored",
code: "u",
hex: "#aaaaaa",
mixing: {
any: "any",
},
}, },
red: {
// 0, 255, 0 id: "red",
[c.green]: { code: "r",
[c.blue]: c.cyan, hex: "#ff666a",
// no recipes
[c.yellow]: c.yellow, spawnable: true,
[c.purple]: c.white,
[c.cyan]: c.cyan,
[c.white]: c.white,
}, },
green: {
// 0, 255, 0 id: "green",
[c.blue]: { code: "g",
[c.yellow]: c.white, hex: "#78ff66",
[c.purple]: c.purple, // no recipes
[c.cyan]: c.cyan, spawnable: true,
[c.white]: c.white,
}, },
blue: {
// 255, 255, 0 id: "blue",
[c.yellow]: { code: "b",
[c.purple]: c.white, hex: "#66a7ff",
[c.cyan]: c.white, // no recipes
spawnable: true,
minDistance: 3,
}, },
cyan: {
// 255, 0, 255 id: "cyan",
[c.purple]: { code: "c",
[c.cyan]: c.white, hex: "#00fcff",
mixingFrom: ["green", "blue"],
mixing: {
green: "this",
blue: "this",
},
}, },
magenta: {
// 0, 255, 255 id: "magenta",
[c.cyan]: {}, code: "p",
hex: "#dd66ff",
//// SPECIAL COLORS mixingFrom: ["red", "blue"],
mixing: {
// 255, 255, 255 red: "this",
[c.white]: { blue: "this",
// auto },
}, },
yellow: {
// X, X, X id: "yellow",
[c.uncolored]: { code: "y",
// auto hex: "#fcf52a",
mixingFrom: ["red", "green"],
mixing: {
red: "this",
green: "this",
},
},
white: {
id: "white",
code: "w",
hex: "#ffffff",
mixing: {
any: "white",
},
mixingFrom: [
["red", "cyan"],
["green", "magenta"],
["blue", "yellow"],
["cyan", "magenta"],
["cyan", "yellow"],
["magenta", "yellow"],
],
}, },
}; };
// Create same color lookups /**
for (const color in enumColors) { * @param {Object} colorData
enumColorMixingResults[color][color] = color; * @property {string} colorData.id
* @property {string} colorData.code
// Anything with white is white again * @property {string} colorData.hex
enumColorMixingResults[color][c.white] = c.white; * @property {Object.<string, string>} [colorData.mixing]
* @property {string[2][] | string[2]} [colorData.mixingFrom]
// Anything with uncolored is the same color */
enumColorMixingResults[color][c.uncolored] = color; function registerColor(colorData) {
allColorData[colorData.id] = colorData;
} }
// Create reverse lookup and check color mixing lookups export let allowColorMixingMismatch = false;
for (const colorA in enumColorMixingResults) { export let allowColorMixingOverride = false;
for (const colorB in enumColorMixingResults[colorA]) { export let allowColorMixingMissingSource = false;
const resultColor = enumColorMixingResults[colorA][colorB]; export let allowColorMixingMissingTarget = false;
if (!enumColorMixingResults[colorB]) {
enumColorMixingResults[colorB] = { for (let data of customColors) {
[colorA]: resultColor, registerColor(data);
}; }
} else {
const existingResult = enumColorMixingResults[colorB][colorA]; const mix = enumColorMixingResults;
if (existingResult && existingResult !== resultColor) {
assertAlways( initColors();
false,
"invalid color mixing configuration, " + export function initColors() {
colorA +
" + " + for (let c1 in allColorData) {
colorB + let data = allColorData[c1];
" is " + assert(data);
resultColor + assert(data.id == c1);
" but " + assert(data.code.toLowerCase() == data.code);
colorB + if (data.disabled) {
" + " + continue;
colorA + }
" is " + if (data.spawnable && !data.minDistance) {
existingResult data.minDistance = 0;
); }
enumColors[c1] = c1;
enumColorToShortcode[c1] = data.code;
enumShortcodeToColor[data.code] = c1;
enumColorsToHexCode[c1] = data.hex;
if (!mix[c1]) {
mix[c1] = {};
}
let mixing = mix[c1];
if (data.mixing) {
for (let c2 in data.mixing) {
if (c2 == "any") {
continue;
}
let result = data.mixing[c2] == "this" ? c1 : data.mixing[c2] == "any" ? c2 : data.mixing[c2];
if (mixing[c2] && mixing[c2] != result) {
if (!allowColorMixingOverride) {
assertAlways(
false,
`Color mixing recipe overrides are not implemented (${c1}+${c2}=${mixing[c2]}->${result})`
);
}
}
mixing[c2] = result;
}
}
}
for (let id in allColorData) {
let data = allColorData[id];
let mixingFrom = !data.mixingFrom
? []
: data.mixingFrom[0] instanceof Array
? data.mixingFrom
: [data.mixingFrom];
for (let [c1, c2] of mixingFrom) {
if (!c[c1] || !c[c2]) {
if (!allowColorMixingMissingSource) {
assertAlways(false, `Color mixing recipe source is not known (${c1}+${c2}=${id})`);
}
continue;
}
if (mix[c1][c2] && mix[c1][c2] != id) {
// TODO
throw "wut";
}
if (mix[c2][c1] && mix[c2][c1] != id) {
// TODO
throw "wut";
}
mix[c1][c2] = id;
}
}
for (let c1 in c) {
for (let c2 in c) {
if (mix[c1][c2] != mix[c2][c1]) {
if (mix[c1][c2] && mix[c2][c1] && !allowColorMixingMismatch) {
assertAlways(
false,
`Color mixing recipe result mismatch (${c1}+${c2}=${mix[c1][c2]}/${mix[c2][c1]}}`
);
}
mix[c1][c2] = mix[c2][c1] = mix[c1][c2] || mix[c2][c1];
}
}
}
for (let c1 in c) {
if (!mix[c1][c1]) {
mix[c1][c1] = c1;
}
}
let anyPairs = {};
for (let c1 in c) {
let mixing = allColorData[c1].mixing;
if (!mixing || !mixing.any) {
continue;
}
for (let c2 in c) {
if (mix[c1][c2] || mix[c2][c1]) {
continue;
}
if (anyPairs[`${c1}+${c2}`]) {
throw "wut";
}
anyPairs[`${c1}+${c2}`] = anyPairs[`${c2}+${c1}`] = true;
mix[c1][c2] = mix[c2][c1] = mixing.any == "any" ? c2 : mixing.any;
}
}
for (let c1 in c) {
for (let c2 in c) {
if (!mix[c1][c2]) {
assertAlways(false, "Color mixing of", c1, "with", c2, "is not defined");
} }
enumColorMixingResults[colorB][colorA] = resultColor;
}
}
}
for (const colorA in enumColorMixingResults) {
for (const colorB in enumColorMixingResults) {
if (!enumColorMixingResults[colorA][colorB]) {
assertAlways(false, "Color mixing of", colorA, "with", colorB, "is not defined");
} }
} }
} }

View File

@ -0,0 +1,29 @@
/** @enum {string} */
export const customColors = [];
/**
* @param {Object} colorData
* @param {string} colorData.id
* @param {string} colorData.code
* @param {string} colorData.hex
* @param {string[][] | string[]} [colorData.mixingFrom]
* @param {Object.<string, string>} [colorData.mixing]
* @param {boolean} [colorData.spawnable]
* @param {number} [colorData.minDistance]
*/
export function registerCustomColor(colorData) {
customColors.push(colorData);
}
registerCustomColor({
id: "black",
code: "k",
hex: "#333333",
mixing: {
white: "uncolored",
uncolored: "uncolored",
any: "black",
},
spawnable: true,
minDistance: 5,
});

View File

@ -33,7 +33,7 @@ export class ColorItem extends BaseItem {
} }
getBackgroundColorAsResource() { getBackgroundColorAsResource() {
return THEME.map.resources[this.color]; return THEME.map.resources[this.color] || THEME.map.resources.shape;
} }
/** /**

View File

@ -8,7 +8,7 @@ import { createLogger } from "../core/logging";
import { clamp, fastArrayDeleteValueIfContained, make2DUndefinedArray } from "../core/utils"; import { clamp, fastArrayDeleteValueIfContained, make2DUndefinedArray } from "../core/utils";
import { Vector } from "../core/vector"; import { Vector } from "../core/vector";
import { BaseItem } from "./base_item"; import { BaseItem } from "./base_item";
import { enumColors } from "./colors"; import { enumColors, allColorData } from "./colors";
import { Entity } from "./entity"; import { Entity } from "./entity";
import { ColorItem } from "./items/color_item"; import { ColorItem } from "./items/color_item";
import { ShapeItem } from "./items/shape_item"; import { ShapeItem } from "./items/shape_item";
@ -136,9 +136,16 @@ export class MapChunk {
*/ */
internalGenerateColorPatch(rng, colorPatchSize, distanceToOriginInChunks) { internalGenerateColorPatch(rng, colorPatchSize, distanceToOriginInChunks) {
// First, determine available colors // First, determine available colors
let availableColors = [enumColors.red, enumColors.green]; let availableColors = [];
if (distanceToOriginInChunks > 2) { for (let c in enumColors) {
availableColors.push(enumColors.blue); c = enumColors[c];
if (
allColorData[c] &&
allColorData[c].spawnable &&
allColorData[c].minDistance <= distanceToOriginInChunks
) {
availableColors.push(c);
}
} }
this.internalGeneratePatch(rng, colorPatchSize, new ColorItem(rng.choice(availableColors))); this.internalGeneratePatch(rng, colorPatchSize, new ColorItem(rng.choice(availableColors)));
} }