1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-06-13 13:04:03 +00:00

Merge branch 'remove-enum-sub-shape' of github.com:bjornstar/shapez.io into enum-removal-super-branch

This commit is contained in:
Bjorn Stromberg 2020-08-05 16:32:21 +09:00
commit bd4ac54bd0
5 changed files with 125 additions and 137 deletions

View File

@ -4,7 +4,7 @@ import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/ut
import { BasicSerializableObject, types } from "../savegame/serialization"; import { BasicSerializableObject, types } from "../savegame/serialization";
import { colors } from "./colors"; import { colors } from "./colors";
import { enumItemProcessorTypes } from "./components/item_processor"; import { enumItemProcessorTypes } from "./components/item_processor";
import { enumSubShape, ShapeDefinition } from "./shape_definition"; import { subShapes, ShapeDefinition } from "./shape_definition";
import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals"; import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals";
import { UPGRADES, blueprintShape } from "./upgrades"; import { UPGRADES, blueprintShape } from "./upgrades";
@ -333,8 +333,7 @@ export class HubGoals extends BasicSerializableObject {
let layers = []; let layers = [];
const randomColor = () => randomChoice(colors); const randomColor = () => randomChoice(colors);
// @ts-ignore const randomShape = () => randomChoice(subShapes);
const randomShape = () => randomChoice(Object.values(enumSubShape));
let anyIsMissingTwo = false; let anyIsMissingTwo = false;

View File

@ -6,6 +6,8 @@ import { ShapeDefinition } from "../../shape_definition";
import { BaseHUDPart } from "../base_hud_part"; import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach"; import { DynamicDomAttach } from "../dynamic_dom_attach";
/** @typedef {import("../../shape_definition").ShapeLayer} ShapeLayer **/
const copy = require("clipboard-copy"); const copy = require("clipboard-copy");
export class HUDShapeViewer extends BaseHUDPart { export class HUDShapeViewer extends BaseHUDPart {
@ -82,12 +84,13 @@ export class HUDShapeViewer extends BaseHUDPart {
this.currentShapeKey = definition.getHash(); this.currentShapeKey = definition.getHash();
const layers = definition.layers; const layers = definition.layers;
this.contentDiv.setAttribute("data-layers", layers.length); this.contentDiv.setAttribute("data-layers", `${layers.length}`);
for (let i = 0; i < layers.length; ++i) { for (let i = 0; i < layers.length; ++i) {
const layerElem = makeDiv(this.renderArea, null, ["layer", "layer-" + i]); const layerElem = makeDiv(this.renderArea, null, ["layer", "layer-" + i]);
let fakeLayers = []; /** @type {Array<ShapeLayer>} **/
const fakeLayers = [];
for (let k = 0; k < i; ++k) { for (let k = 0; k < i; ++k) {
fakeLayers.push([null, null, null, null]); fakeLayers.push([null, null, null, null]);
} }

View File

@ -6,17 +6,42 @@ import { BaseItem } from "./base_item";
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";
import { enumSubShape } from "./shape_definition"; import { subShapes } from "./shape_definition";
import { RandomNumberGenerator } from "../core/rng"; import { RandomNumberGenerator } from "../core/rng";
/** /**
* @typedef {import("./root").GameRoot} GameRoot * @typedef {import("./root").GameRoot} GameRoot
* @typedef {import("./root").Layer} Layer * @typedef {import("./root").Layer} Layer
* @typedef {import("./colors").Color} Color
* @typedef {import("./shape_definition").SubShape} SubShape
*/ */
const logger = createLogger("map_chunk"); const logger = createLogger("map_chunk");
/** @typedef {import("./colors").Color} Color **/ /**
* Chooses a random shape with the given weights
* @param {RandomNumberGenerator} rng
* @param {Record<SubShape, number>} weights
* @returns {SubShape}
*/
function generateRandomSubShape(rng, weights) {
const sum = Object.values(weights).reduce((a, b) => a + b, 0);
const chosenNumber = rng.nextIntRange(0, sum - 1);
let accumulated = 0;
for (let i = 0; i < subShapes.length; i += 1) {
const shape = subShapes[i];
const weight = weights[shape];
if (accumulated + weight > chosenNumber) {
return shape;
}
accumulated += weight;
}
logger.error("Failed to find matching shape in chunk generation");
return "circle";
}
export class MapChunk { export class MapChunk {
/** /**
@ -122,8 +147,6 @@ export class MapChunk {
avgPos.y += y; avgPos.y += y;
} }
} }
} else {
// logger.warn("Tried to spawn resource out of chunk");
} }
} }
} }
@ -159,84 +182,57 @@ export class MapChunk {
* @param {number} distanceToOriginInChunks * @param {number} distanceToOriginInChunks
*/ */
internalGenerateShapePatch(rng, shapePatchSize, distanceToOriginInChunks) { internalGenerateShapePatch(rng, shapePatchSize, distanceToOriginInChunks) {
/** @type {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} */ /** @type {[SubShape, SubShape, SubShape, SubShape]} */
let subShapes = null; let subShapes = null;
let weights = {};
// Later there is a mix of everything // Later there is a mix of everything
weights = { /** @type {Record<SubShape, number>} **/
[enumSubShape.rect]: 100, const weights = {
[enumSubShape.circle]: Math.round(50 + clamp(distanceToOriginInChunks * 2, 0, 50)), rect: 100,
[enumSubShape.star]: Math.round(20 + clamp(distanceToOriginInChunks, 0, 30)), circle: Math.round(50 + clamp(distanceToOriginInChunks * 2, 0, 50)),
[enumSubShape.windmill]: Math.round(6 + clamp(distanceToOriginInChunks / 2, 0, 20)), star: Math.round(20 + clamp(distanceToOriginInChunks, 0, 30)),
windmill: Math.round(6 + clamp(distanceToOriginInChunks / 2, 0, 20)),
}; };
if (distanceToOriginInChunks < 7) { if (distanceToOriginInChunks < 7) {
// Initial chunks can not spawn the good stuff // Initial chunks can not spawn the good stuff
weights[enumSubShape.star] = 0; weights.star = 0;
weights[enumSubShape.windmill] = 0; weights.windmill = 0;
} }
if (distanceToOriginInChunks < 10) { if (distanceToOriginInChunks < 10) {
// Initial chunk patches always have the same shape // Initial chunk patches always have the same subshape
const subShape = this.internalGenerateRandomSubShape(rng, weights); const subShape = generateRandomSubShape(rng, weights);
subShapes = [subShape, subShape, subShape, subShape]; subShapes = [subShape, subShape, subShape, subShape];
} else if (distanceToOriginInChunks < 15) { } else if (distanceToOriginInChunks < 15) {
// Later patches can also have mixed ones // Later patches can have mixed halves
const subShapeA = this.internalGenerateRandomSubShape(rng, weights); const subShapeA = generateRandomSubShape(rng, weights);
const subShapeB = this.internalGenerateRandomSubShape(rng, weights); const subShapeB = generateRandomSubShape(rng, weights);
subShapes = [subShapeA, subShapeA, subShapeB, subShapeB]; subShapes = [subShapeA, subShapeA, subShapeB, subShapeB];
} else { } else {
// Finally there is a mix of everything // Finally there is a mix of everything
subShapes = [ subShapes = [
this.internalGenerateRandomSubShape(rng, weights), generateRandomSubShape(rng, weights),
this.internalGenerateRandomSubShape(rng, weights), generateRandomSubShape(rng, weights),
this.internalGenerateRandomSubShape(rng, weights), generateRandomSubShape(rng, weights),
this.internalGenerateRandomSubShape(rng, weights), generateRandomSubShape(rng, weights),
]; ];
} }
// Makes sure windmills never spawn as whole // Makes sure windmills never spawn as whole
let windmillCount = 0; const windmillCount = subShapes.reduce((sum, subShape) => {
for (let i = 0; i < subShapes.length; ++i) { return sum + Number(subShape === "windmill");
if (subShapes[i] === enumSubShape.windmill) { }, 0);
++windmillCount;
}
}
if (windmillCount > 1) { if (windmillCount > 1) {
subShapes[0] = enumSubShape.rect; subShapes[0] = "rect";
subShapes[1] = enumSubShape.rect; subShapes[1] = "rect";
} }
const definition = this.root.shapeDefinitionMgr.getDefinitionFromSimpleShapes(subShapes); const definition = this.root.shapeDefinitionMgr.getDefinitionFromSimpleShapes(subShapes);
this.internalGeneratePatch(rng, shapePatchSize, new ShapeItem(definition)); this.internalGeneratePatch(rng, shapePatchSize, new ShapeItem(definition));
} }
/**
* Chooses a random shape with the given weights
* @param {RandomNumberGenerator} rng
* @param {Object.<enumSubShape, number>} weights
* @returns {enumSubShape}
*/
internalGenerateRandomSubShape(rng, weights) {
// @ts-ignore
const sum = Object.values(weights).reduce((a, b) => a + b, 0);
const chosenNumber = rng.nextIntRange(0, sum - 1);
let accumulated = 0;
for (const key in weights) {
const weight = weights[key];
if (accumulated + weight > chosenNumber) {
return key;
}
accumulated += weight;
}
logger.error("Failed to find matching shape in chunk generation");
return enumSubShape.circle;
}
/** /**
* Generates the lower layer "terrain" * Generates the lower layer "terrain"
*/ */

View File

@ -14,12 +14,40 @@ const logger = createLogger("shape_definition");
/** /**
* @typedef {import("./colors").Color} Color * @typedef {import("./colors").Color} Color
* @typedef {{ subShape: enumSubShape, color: Color }} ShapeLayerItem *
* @typedef {"rect" | "circle" | "star" | "windmill"} SubShape
* @typedef {"R" | "C" | "S" | "W"} SubShapeShortCode
* @typedef {{
* subShape: SubShape,
* color: Color,
* }} ShapeLayerItem
* *
* Order is Q1 (tr), Q2(br), Q3(bl), Q4(tl) * Order is Q1 (tr), Q2(br), Q3(bl), Q4(tl)
* @typedef {[ShapeLayerItem?, ShapeLayerItem?, ShapeLayerItem?, ShapeLayerItem?]} ShapeLayer * @typedef {[ShapeLayerItem?, ShapeLayerItem?, ShapeLayerItem?, ShapeLayerItem?]} ShapeLayer
*/ */
/** @type {SubShape[]} **/
export const subShapes = ["rect", "circle", "star", "windmill"];
/** @type {SubShapeShortCode[]} **/
export const subShapeShortCodes = ["R", "C", "S", "W"];
/** @type {Record<SubShape, SubShapeShortCode>} **/
export const subShapeShortCodeMap = {
rect: "R",
circle: "C",
star: "S",
windmill: "W",
};
/** @type {Record<SubShapeShortCode, SubShape>} **/
export const shortCodeSubShapeMap = {
R: "rect",
C: "circle",
S: "star",
W: "windmill",
};
const arrayQuadrantIndexToOffset = [ const arrayQuadrantIndexToOffset = [
new Vector(1, -1), // tr new Vector(1, -1), // tr
new Vector(1, 1), // br new Vector(1, 1), // br
@ -27,44 +55,6 @@ const arrayQuadrantIndexToOffset = [
new Vector(-1, -1), // tl new Vector(-1, -1), // tl
]; ];
/** @enum {string} */
export const enumSubShape = {
rect: "rect",
circle: "circle",
star: "star",
windmill: "windmill",
};
/** @enum {string} */
export const enumSubShapeToShortcode = {
[enumSubShape.rect]: "R",
[enumSubShape.circle]: "C",
[enumSubShape.star]: "S",
[enumSubShape.windmill]: "W",
};
/** @enum {enumSubShape} */
export const enumShortcodeToSubShape = {};
for (const key in enumSubShapeToShortcode) {
enumShortcodeToSubShape[enumSubShapeToShortcode[key]] = key;
}
/**
* Converts the given parameters to a valid shape definition
* @param {*} layers
* @returns {Array<ShapeLayer>}
*/
export function createSimpleShape(layers) {
layers.forEach(layer => {
layer.forEach(item => {
if (item) {
item.color = item.color || "uncolored";
}
});
});
return layers;
}
/** /**
* Cache which shapes are valid short keys and which not * Cache which shapes are valid short keys and which not
* @type {Map<string, boolean>} * @type {Map<string, boolean>}
@ -72,29 +62,7 @@ export function createSimpleShape(layers) {
const SHORT_KEY_CACHE = new Map(); const SHORT_KEY_CACHE = new Map();
export class ShapeDefinition extends BasicSerializableObject { export class ShapeDefinition extends BasicSerializableObject {
static getId() {
return "ShapeDefinition";
}
static getSchema() {
return {};
}
deserialize(data) {
const errorCode = super.deserialize(data);
if (errorCode) {
return errorCode;
}
const definition = ShapeDefinition.fromShortKey(data);
this.layers = definition.layers;
}
serialize() {
return this.getHash();
}
/** /**
*
* @param {object} param0 * @param {object} param0
* @param {Array<ShapeLayer>=} param0.layers * @param {Array<ShapeLayer>=} param0.layers
*/ */
@ -113,6 +81,14 @@ export class ShapeDefinition extends BasicSerializableObject {
this.bufferGenerator = null; this.bufferGenerator = null;
} }
static getId() {
return "ShapeDefinition";
}
static getSchema() {
return {};
}
/** /**
* Generates the definition from the given short key * Generates the definition from the given short key
* @param {string} key * @param {string} key
@ -120,7 +96,8 @@ export class ShapeDefinition extends BasicSerializableObject {
*/ */
static fromShortKey(key) { static fromShortKey(key) {
const sourceLayers = key.split(":"); const sourceLayers = key.split(":");
let layers = []; const layers = [];
for (let i = 0; i < sourceLayers.length; ++i) { for (let i = 0; i < sourceLayers.length; ++i) {
const text = sourceLayers[i]; const text = sourceLayers[i];
assert(text.length === 8, "Invalid shape short key: " + key); assert(text.length === 8, "Invalid shape short key: " + key);
@ -129,8 +106,10 @@ export class ShapeDefinition extends BasicSerializableObject {
const quads = [null, null, null, null]; const quads = [null, null, null, null];
for (let quad = 0; quad < 4; ++quad) { for (let quad = 0; quad < 4; ++quad) {
const shapeText = text[quad * 2 + 0]; const shapeText = text[quad * 2 + 0];
const subShape = enumShortcodeToSubShape[shapeText];
const subShape = shortCodeSubShapeMap[shapeText];
const color = shortColorColorMap[text[quad * 2 + 1]]; const color = shortColorColorMap[text[quad * 2 + 1]];
if (subShape) { if (subShape) {
assert(color, "Invalid shape short key:", key); assert(color, "Invalid shape short key:", key);
quads[quad] = { quads[quad] = {
@ -173,7 +152,8 @@ export class ShapeDefinition extends BasicSerializableObject {
*/ */
static isValidShortKeyInternal(key) { static isValidShortKeyInternal(key) {
const sourceLayers = key.split(":"); const sourceLayers = key.split(":");
let layers = []; const layers = [];
for (let i = 0; i < sourceLayers.length; ++i) { for (let i = 0; i < sourceLayers.length; ++i) {
const text = sourceLayers[i]; const text = sourceLayers[i];
if (text.length !== 8) { if (text.length !== 8) {
@ -186,7 +166,8 @@ export class ShapeDefinition extends BasicSerializableObject {
for (let quad = 0; quad < 4; ++quad) { for (let quad = 0; quad < 4; ++quad) {
const shapeText = text[quad * 2 + 0]; const shapeText = text[quad * 2 + 0];
const colorText = text[quad * 2 + 1]; const colorText = text[quad * 2 + 1];
const subShape = enumShortcodeToSubShape[shapeText];
const subShape = shortCodeSubShapeMap[shapeText];
const color = shortColorColorMap[colorText]; const color = shortColorColorMap[colorText];
// Valid shape // Valid shape
@ -225,6 +206,19 @@ export class ShapeDefinition extends BasicSerializableObject {
return true; return true;
} }
deserialize(data) {
const errorCode = super.deserialize(data);
if (errorCode) {
return errorCode;
}
const definition = ShapeDefinition.fromShortKey(data);
this.layers = definition.layers;
}
serialize() {
return this.getHash();
}
/** /**
* Internal method to clone the shape definition * Internal method to clone the shape definition
* @returns {Array<ShapeLayer>} * @returns {Array<ShapeLayer>}
@ -257,7 +251,7 @@ export class ShapeDefinition extends BasicSerializableObject {
for (let quadrant = 0; quadrant < layer.length; ++quadrant) { for (let quadrant = 0; quadrant < layer.length; ++quadrant) {
const item = layer[quadrant]; const item = layer[quadrant];
if (item) { if (item) {
id += enumSubShapeToShortcode[item.subShape] + colorShortColorMap[item.color]; id += subShapeShortCodeMap[item.subShape] + colorShortColorMap[item.color];
} else { } else {
id += "--"; id += "--";
} }
@ -359,7 +353,7 @@ export class ShapeDefinition extends BasicSerializableObject {
const insetPadding = 0.0; const insetPadding = 0.0;
switch (subShape) { switch (subShape) {
case enumSubShape.rect: { case "rect": {
context.beginPath(); context.beginPath();
const dims = quadrantSize * layerScale; const dims = quadrantSize * layerScale;
context.rect( context.rect(
@ -371,7 +365,7 @@ export class ShapeDefinition extends BasicSerializableObject {
break; break;
} }
case enumSubShape.star: { case "star": {
context.beginPath(); context.beginPath();
const dims = quadrantSize * layerScale; const dims = quadrantSize * layerScale;
@ -387,7 +381,7 @@ export class ShapeDefinition extends BasicSerializableObject {
break; break;
} }
case enumSubShape.windmill: { case "windmill": {
context.beginPath(); context.beginPath();
const dims = quadrantSize * layerScale; const dims = quadrantSize * layerScale;
@ -402,7 +396,7 @@ export class ShapeDefinition extends BasicSerializableObject {
break; break;
} }
case enumSubShape.circle: { case "circle": {
context.beginPath(); context.beginPath();
context.moveTo(insetPadding + -quadrantHalfSize, -insetPadding + quadrantHalfSize); context.moveTo(insetPadding + -quadrantHalfSize, -insetPadding + quadrantHalfSize);
context.arc( context.arc(

View File

@ -1,13 +1,11 @@
import { BasicSerializableObject } from "../savegame/serialization"; import { BasicSerializableObject } from "../savegame/serialization";
import { GameRoot } from "./root"; import { GameRoot } from "./root";
import { ShapeDefinition, enumSubShape } from "./shape_definition"; import { ShapeDefinition } from "./shape_definition";
import { createLogger } from "../core/logging";
const logger = createLogger("shape_definition_manager");
/** /**
* @typedef {import("./colors").Color} Color * @typedef {import("./colors").Color} Color
* @typedef {import("./shape_definition").ShapeLayer} ShapeLayer * @typedef {import("./shape_definition").ShapeLayer} ShapeLayer
* @typedef {import("./shape_definition").SubShape} SubShape
*/ */
export class ShapeDefinitionManager extends BasicSerializableObject { export class ShapeDefinitionManager extends BasicSerializableObject {
@ -227,18 +225,16 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
return this.shapeKeyToDefinition[id]; return this.shapeKeyToDefinition[id];
} }
this.shapeKeyToDefinition[id] = definition; this.shapeKeyToDefinition[id] = definition;
// logger.log("Registered shape with key (2)", id);
return definition; return definition;
} }
/** /**
* *
* @param {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} subShapes * @param {[SubShape, SubShape, SubShape, SubShape]} subShapes
* @returns {ShapeDefinition} * @returns {ShapeDefinition}
*/ */
getDefinitionFromSimpleShapes(subShapes, color = "uncolored") { getDefinitionFromSimpleShapes(subShapes, color = "uncolored") {
const shapeLayer = /** @type {ShapeLayer} */ (subShapes.map(subShape => ({ subShape, color }))); const shapeLayer = /** @type {ShapeLayer} */ (subShapes.map(subShape => ({ subShape, color })));
return this.registerOrReturnHandle(new ShapeDefinition({ layers: [shapeLayer] })); return this.registerOrReturnHandle(new ShapeDefinition({ layers: [shapeLayer] }));
} }
} }