mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
Replace enumSubShape with SubShape
This commit is contained in:
parent
235c380de1
commit
c13303185b
@ -5,7 +5,7 @@ import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
import { enumColors } from "./colors";
|
||||
import { enumItemProcessorTypes } from "./components/item_processor";
|
||||
import { GameRoot, enumLayer } from "./root";
|
||||
import { enumSubShape, ShapeDefinition } from "./shape_definition";
|
||||
import { subShapes, ShapeDefinition } from "./shape_definition";
|
||||
import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals";
|
||||
import { UPGRADES, blueprintShape } from "./upgrades";
|
||||
|
||||
@ -331,7 +331,7 @@ export class HubGoals extends BasicSerializableObject {
|
||||
// @ts-ignore
|
||||
const randomColor = () => randomChoice(Object.values(enumColors));
|
||||
// @ts-ignore
|
||||
const randomShape = () => randomChoice(Object.values(enumSubShape));
|
||||
const randomShape = () => randomChoice(Object.values(subShapes));
|
||||
|
||||
let anyIsMissingTwo = false;
|
||||
|
||||
|
@ -6,6 +6,8 @@ import { ShapeDefinition } from "../../shape_definition";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||
|
||||
/** @typedef {import("../../shape_definition").ShapeLayer} ShapeLayer **/
|
||||
|
||||
const copy = require("clipboard-copy");
|
||||
|
||||
export class HUDShapeViewer extends BaseHUDPart {
|
||||
@ -82,12 +84,13 @@ export class HUDShapeViewer extends BaseHUDPart {
|
||||
this.currentShapeKey = definition.getHash();
|
||||
|
||||
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) {
|
||||
const layerElem = makeDiv(this.renderArea, null, ["layer", "layer-" + i]);
|
||||
|
||||
let fakeLayers = [];
|
||||
/** @type {Array<ShapeLayer>} **/
|
||||
const fakeLayers = [];
|
||||
for (let k = 0; k < i; ++k) {
|
||||
fakeLayers.push([null, null, null, null]);
|
||||
}
|
||||
|
@ -8,11 +8,38 @@ import { enumColors } from "./colors";
|
||||
import { Entity } from "./entity";
|
||||
import { ColorItem } from "./items/color_item";
|
||||
import { ShapeItem } from "./items/shape_item";
|
||||
import { enumSubShape } from "./shape_definition";
|
||||
import { subShapes } from "./shape_definition";
|
||||
import { RandomNumberGenerator } from "../core/rng";
|
||||
|
||||
/** @typedef {import("./shape_definition").SubShape} SubShape **/
|
||||
|
||||
const logger = createLogger("map_chunk");
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
/**
|
||||
*
|
||||
@ -117,8 +144,6 @@ export class MapChunk {
|
||||
avgPos.y += y;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// logger.warn("Tried to spawn resource out of chunk");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,84 +178,57 @@ export class MapChunk {
|
||||
* @param {number} distanceToOriginInChunks
|
||||
*/
|
||||
internalGenerateShapePatch(rng, shapePatchSize, distanceToOriginInChunks) {
|
||||
/** @type {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} */
|
||||
/** @type {[SubShape, SubShape, SubShape, SubShape]} */
|
||||
let subShapes = null;
|
||||
|
||||
let weights = {};
|
||||
|
||||
// Later there is a mix of everything
|
||||
weights = {
|
||||
[enumSubShape.rect]: 100,
|
||||
[enumSubShape.circle]: Math.round(50 + clamp(distanceToOriginInChunks * 2, 0, 50)),
|
||||
[enumSubShape.star]: Math.round(20 + clamp(distanceToOriginInChunks, 0, 30)),
|
||||
[enumSubShape.windmill]: Math.round(6 + clamp(distanceToOriginInChunks / 2, 0, 20)),
|
||||
/** @type {Record<SubShape, number>} **/
|
||||
const weights = {
|
||||
rect: 100,
|
||||
circle: Math.round(50 + clamp(distanceToOriginInChunks * 2, 0, 50)),
|
||||
star: Math.round(20 + clamp(distanceToOriginInChunks, 0, 30)),
|
||||
windmill: Math.round(6 + clamp(distanceToOriginInChunks / 2, 0, 20)),
|
||||
};
|
||||
|
||||
if (distanceToOriginInChunks < 7) {
|
||||
// Initial chunks can not spawn the good stuff
|
||||
weights[enumSubShape.star] = 0;
|
||||
weights[enumSubShape.windmill] = 0;
|
||||
weights.star = 0;
|
||||
weights.windmill = 0;
|
||||
}
|
||||
|
||||
if (distanceToOriginInChunks < 10) {
|
||||
// Initial chunk patches always have the same shape
|
||||
const subShape = this.internalGenerateRandomSubShape(rng, weights);
|
||||
// Initial chunk patches always have the same subshape
|
||||
const subShape = generateRandomSubShape(rng, weights);
|
||||
subShapes = [subShape, subShape, subShape, subShape];
|
||||
} else if (distanceToOriginInChunks < 15) {
|
||||
// Later patches can also have mixed ones
|
||||
const subShapeA = this.internalGenerateRandomSubShape(rng, weights);
|
||||
const subShapeB = this.internalGenerateRandomSubShape(rng, weights);
|
||||
// Later patches can have mixed halves
|
||||
const subShapeA = generateRandomSubShape(rng, weights);
|
||||
const subShapeB = generateRandomSubShape(rng, weights);
|
||||
subShapes = [subShapeA, subShapeA, subShapeB, subShapeB];
|
||||
} else {
|
||||
// Finally there is a mix of everything
|
||||
subShapes = [
|
||||
this.internalGenerateRandomSubShape(rng, weights),
|
||||
this.internalGenerateRandomSubShape(rng, weights),
|
||||
this.internalGenerateRandomSubShape(rng, weights),
|
||||
this.internalGenerateRandomSubShape(rng, weights),
|
||||
generateRandomSubShape(rng, weights),
|
||||
generateRandomSubShape(rng, weights),
|
||||
generateRandomSubShape(rng, weights),
|
||||
generateRandomSubShape(rng, weights),
|
||||
];
|
||||
}
|
||||
|
||||
// Makes sure windmills never spawn as whole
|
||||
let windmillCount = 0;
|
||||
for (let i = 0; i < subShapes.length; ++i) {
|
||||
if (subShapes[i] === enumSubShape.windmill) {
|
||||
++windmillCount;
|
||||
}
|
||||
}
|
||||
const windmillCount = subShapes.reduce((sum, subShape) => {
|
||||
return sum + Number(subShape === "windmill");
|
||||
}, 0);
|
||||
|
||||
if (windmillCount > 1) {
|
||||
subShapes[0] = enumSubShape.rect;
|
||||
subShapes[1] = enumSubShape.rect;
|
||||
subShapes[0] = "rect";
|
||||
subShapes[1] = "rect";
|
||||
}
|
||||
|
||||
const definition = this.root.shapeDefinitionMgr.getDefinitionFromSimpleShapes(subShapes);
|
||||
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"
|
||||
*/
|
||||
|
@ -19,17 +19,42 @@ const rusha = require("rusha");
|
||||
const logger = createLogger("shape_definition");
|
||||
|
||||
/**
|
||||
* @typedef {"rect" | "circle" | "star" | "windmill"} SubShape
|
||||
* @typedef {"R" | "C" | "S" | "W"} SubShapeShortCode
|
||||
*
|
||||
*
|
||||
*
|
||||
* @typedef {{
|
||||
* subShape: enumSubShape,
|
||||
* subShape: SubShape,
|
||||
* color: enumColors,
|
||||
* }} ShapeLayerItem
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* Order is Q1 (tr), Q2(br), Q3(bl), Q4(tl)
|
||||
* @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 = [
|
||||
new Vector(1, -1), // tr
|
||||
new Vector(1, 1), // br
|
||||
@ -37,44 +62,6 @@ const arrayQuadrantIndexToOffset = [
|
||||
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<import("./shape_definition").ShapeLayer>}
|
||||
*/
|
||||
export function createSimpleShape(layers) {
|
||||
layers.forEach(layer => {
|
||||
layer.forEach(item => {
|
||||
if (item) {
|
||||
item.color = item.color || enumColors.uncolored;
|
||||
}
|
||||
});
|
||||
});
|
||||
return layers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache which shapes are valid short keys and which not
|
||||
* @type {Map<string, boolean>}
|
||||
@ -82,29 +69,7 @@ export function createSimpleShape(layers) {
|
||||
const SHORT_KEY_CACHE = new Map();
|
||||
|
||||
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 {Array<ShapeLayer>=} param0.layers
|
||||
*/
|
||||
@ -123,6 +88,14 @@ export class ShapeDefinition extends BasicSerializableObject {
|
||||
this.bufferGenerator = null;
|
||||
}
|
||||
|
||||
static getId() {
|
||||
return "ShapeDefinition";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the definition from the given short key
|
||||
* @param {string} key
|
||||
@ -130,7 +103,8 @@ export class ShapeDefinition extends BasicSerializableObject {
|
||||
*/
|
||||
static fromShortKey(key) {
|
||||
const sourceLayers = key.split(":");
|
||||
let layers = [];
|
||||
const layers = [];
|
||||
|
||||
for (let i = 0; i < sourceLayers.length; ++i) {
|
||||
const text = sourceLayers[i];
|
||||
assert(text.length === 8, "Invalid shape short key: " + key);
|
||||
@ -139,7 +113,7 @@ export class ShapeDefinition extends BasicSerializableObject {
|
||||
const quads = [null, null, null, null];
|
||||
for (let quad = 0; quad < 4; ++quad) {
|
||||
const shapeText = text[quad * 2 + 0];
|
||||
const subShape = enumShortcodeToSubShape[shapeText];
|
||||
const subShape = shortCodeSubShapeMap[shapeText];
|
||||
const color = enumShortcodeToColor[text[quad * 2 + 1]];
|
||||
if (subShape) {
|
||||
assert(color, "Invalid shape short key:", key);
|
||||
@ -183,7 +157,8 @@ export class ShapeDefinition extends BasicSerializableObject {
|
||||
*/
|
||||
static isValidShortKeyInternal(key) {
|
||||
const sourceLayers = key.split(":");
|
||||
let layers = [];
|
||||
const layers = [];
|
||||
|
||||
for (let i = 0; i < sourceLayers.length; ++i) {
|
||||
const text = sourceLayers[i];
|
||||
if (text.length !== 8) {
|
||||
@ -196,7 +171,7 @@ export class ShapeDefinition extends BasicSerializableObject {
|
||||
for (let quad = 0; quad < 4; ++quad) {
|
||||
const shapeText = text[quad * 2 + 0];
|
||||
const colorText = text[quad * 2 + 1];
|
||||
const subShape = enumShortcodeToSubShape[shapeText];
|
||||
const subShape = shortCodeSubShapeMap[shapeText];
|
||||
const color = enumShortcodeToColor[colorText];
|
||||
|
||||
// Valid shape
|
||||
@ -235,6 +210,19 @@ export class ShapeDefinition extends BasicSerializableObject {
|
||||
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
|
||||
* @returns {Array<ShapeLayer>}
|
||||
@ -267,7 +255,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 += subShapeShortCodeMap[item.subShape] + enumColorToShortcode[item.color];
|
||||
} else {
|
||||
id += "--";
|
||||
}
|
||||
@ -369,7 +357,7 @@ export class ShapeDefinition extends BasicSerializableObject {
|
||||
const insetPadding = 0.0;
|
||||
|
||||
switch (subShape) {
|
||||
case enumSubShape.rect: {
|
||||
case "rect": {
|
||||
context.beginPath();
|
||||
const dims = quadrantSize * layerScale;
|
||||
context.rect(
|
||||
@ -381,7 +369,7 @@ export class ShapeDefinition extends BasicSerializableObject {
|
||||
|
||||
break;
|
||||
}
|
||||
case enumSubShape.star: {
|
||||
case "star": {
|
||||
context.beginPath();
|
||||
const dims = quadrantSize * layerScale;
|
||||
|
||||
@ -397,7 +385,7 @@ export class ShapeDefinition extends BasicSerializableObject {
|
||||
break;
|
||||
}
|
||||
|
||||
case enumSubShape.windmill: {
|
||||
case "windmill": {
|
||||
context.beginPath();
|
||||
const dims = quadrantSize * layerScale;
|
||||
|
||||
@ -412,7 +400,7 @@ export class ShapeDefinition extends BasicSerializableObject {
|
||||
break;
|
||||
}
|
||||
|
||||
case enumSubShape.circle: {
|
||||
case "circle": {
|
||||
context.beginPath();
|
||||
context.moveTo(insetPadding + -quadrantHalfSize, -insetPadding + quadrantHalfSize);
|
||||
context.arc(
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { BasicSerializableObject } from "../savegame/serialization";
|
||||
import { GameRoot } from "./root";
|
||||
import { ShapeDefinition, enumSubShape } from "./shape_definition";
|
||||
import { createLogger } from "../core/logging";
|
||||
import { ShapeDefinition } from "./shape_definition";
|
||||
import { enumColors } from "./colors";
|
||||
|
||||
const logger = createLogger("shape_definition_manager");
|
||||
/** @typedef {import("./shape_definition").SubShape} SubShape **/
|
||||
/** @typedef {import("./shape_definition").ShapeLayer} ShapeLayer **/
|
||||
|
||||
export class ShapeDefinitionManager extends BasicSerializableObject {
|
||||
static getId() {
|
||||
@ -223,20 +223,16 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
|
||||
return this.shapeKeyToDefinition[id];
|
||||
}
|
||||
this.shapeKeyToDefinition[id] = definition;
|
||||
// logger.log("Registered shape with key (2)", id);
|
||||
return definition;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} subShapes
|
||||
* @param {[SubShape, SubShape, SubShape, SubShape]} subShapes
|
||||
* @returns {ShapeDefinition}
|
||||
*/
|
||||
getDefinitionFromSimpleShapes(subShapes, color = enumColors.uncolored) {
|
||||
const shapeLayer = /** @type {import("./shape_definition").ShapeLayer} */ (subShapes.map(
|
||||
subShape => ({ subShape, color })
|
||||
));
|
||||
|
||||
const shapeLayer = /** @type {ShapeLayer} */ (subShapes.map(subShape => ({ subShape, color })));
|
||||
return this.registerOrReturnHandle(new ShapeDefinition({ layers: [shapeLayer] }));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user