1
0
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:
Bjorn Stromberg 2020-08-04 19:59:35 +09:00
parent 235c380de1
commit c13303185b
5 changed files with 125 additions and 140 deletions

View File

@ -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;

View File

@ -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]);
}

View File

@ -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"
*/

View File

@ -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(

View File

@ -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] }));
}
}