mirror of
https://github.com/tobspr/shapez.io.git
synced 2024-10-27 20:34:29 +00:00
* [WIP] Fix achievements with unlock attempt on init. Fix lint * Add init checks for more achievements * Fix tslint errors * Update CI step to include config.local copy from template if not present
271 lines
9.5 KiB
JavaScript
271 lines
9.5 KiB
JavaScript
import { createLogger } from "../core/logging";
|
|
import { BasicSerializableObject } from "../savegame/serialization";
|
|
import { enumColors } from "./colors";
|
|
import { ShapeItem } from "./items/shape_item";
|
|
import { GameRoot } from "./root";
|
|
import { enumSubShape, ShapeDefinition } from "./shape_definition";
|
|
import { ACHIEVEMENTS } from "../platform/achievement_provider";
|
|
|
|
const logger = createLogger("shape_definition_manager");
|
|
|
|
export class ShapeDefinitionManager extends BasicSerializableObject {
|
|
static getId() {
|
|
return "ShapeDefinitionManager";
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {GameRoot} root
|
|
*/
|
|
constructor(root) {
|
|
super();
|
|
this.root = root;
|
|
|
|
/**
|
|
* Store a cache from key -> definition
|
|
* @type {Object<string, ShapeDefinition>}
|
|
*/
|
|
this.shapeKeyToDefinition = {};
|
|
|
|
/**
|
|
* Store a cache from key -> item
|
|
*/
|
|
this.shapeKeyToItem = {};
|
|
|
|
// Caches operations in the form of 'operation/def1[/def2]'
|
|
/** @type {Object.<string, Array<ShapeDefinition>|ShapeDefinition>} */
|
|
this.operationCache = {};
|
|
}
|
|
|
|
/**
|
|
* Returns a shape instance from a given short key
|
|
* @param {string} hash
|
|
* @returns {ShapeDefinition}
|
|
*/
|
|
getShapeFromShortKey(hash) {
|
|
const cached = this.shapeKeyToDefinition[hash];
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
return (this.shapeKeyToDefinition[hash] = ShapeDefinition.fromShortKey(hash));
|
|
}
|
|
|
|
/**
|
|
* Returns a item instance from a given short key
|
|
* @param {string} hash
|
|
* @returns {ShapeItem}
|
|
*/
|
|
getShapeItemFromShortKey(hash) {
|
|
const cached = this.shapeKeyToItem[hash];
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
const definition = this.getShapeFromShortKey(hash);
|
|
return (this.shapeKeyToItem[hash] = new ShapeItem(definition));
|
|
}
|
|
|
|
/**
|
|
* Returns a shape item for a given definition
|
|
* @param {ShapeDefinition} definition
|
|
* @returns {ShapeItem}
|
|
*/
|
|
getShapeItemFromDefinition(definition) {
|
|
return this.getShapeItemFromShortKey(definition.getHash());
|
|
}
|
|
|
|
/**
|
|
* Registers a new shape definition
|
|
* @param {ShapeDefinition} definition
|
|
*/
|
|
registerShapeDefinition(definition) {
|
|
const id = definition.getHash();
|
|
assert(!this.shapeKeyToDefinition[id], "Shape Definition " + id + " already exists");
|
|
this.shapeKeyToDefinition[id] = definition;
|
|
// logger.log("Registered shape with key", id);
|
|
}
|
|
|
|
/**
|
|
* Generates a definition for splitting a shape definition in two halfs
|
|
* @param {ShapeDefinition} definition
|
|
* @returns {[ShapeDefinition, ShapeDefinition]}
|
|
*/
|
|
shapeActionCutHalf(definition) {
|
|
const key = "cut/" + definition.getHash();
|
|
if (this.operationCache[key]) {
|
|
return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key]);
|
|
}
|
|
const rightSide = definition.cloneFilteredByQuadrants([2, 3]);
|
|
const leftSide = definition.cloneFilteredByQuadrants([0, 1]);
|
|
|
|
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.cutShape, null);
|
|
|
|
return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key] = [
|
|
this.registerOrReturnHandle(rightSide),
|
|
this.registerOrReturnHandle(leftSide),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Generates a definition for splitting a shape definition in four quads
|
|
* @param {ShapeDefinition} definition
|
|
* @returns {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]}
|
|
*/
|
|
shapeActionCutQuad(definition) {
|
|
const key = "cut-quad/" + definition.getHash();
|
|
if (this.operationCache[key]) {
|
|
return /** @type {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]} */ (this
|
|
.operationCache[key]);
|
|
}
|
|
|
|
return /** @type {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]} */ (this.operationCache[
|
|
key
|
|
] = [
|
|
this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([0])),
|
|
this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([1])),
|
|
this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([2])),
|
|
this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([3])),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Generates a definition for rotating a shape clockwise
|
|
* @param {ShapeDefinition} definition
|
|
* @returns {ShapeDefinition}
|
|
*/
|
|
shapeActionRotateCW(definition) {
|
|
const key = "rotate-cw/" + definition.getHash();
|
|
if (this.operationCache[key]) {
|
|
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
|
|
}
|
|
|
|
const rotated = definition.cloneRotateCW();
|
|
|
|
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.rotateShape, null);
|
|
|
|
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
|
|
rotated
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Generates a definition for rotating a shape counter clockwise
|
|
* @param {ShapeDefinition} definition
|
|
* @returns {ShapeDefinition}
|
|
*/
|
|
shapeActionRotateCCW(definition) {
|
|
const key = "rotate-ccw/" + definition.getHash();
|
|
if (this.operationCache[key]) {
|
|
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
|
|
}
|
|
|
|
const rotated = definition.cloneRotateCCW();
|
|
|
|
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
|
|
rotated
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Generates a definition for rotating a shape FL
|
|
* @param {ShapeDefinition} definition
|
|
* @returns {ShapeDefinition}
|
|
*/
|
|
shapeActionRotate180(definition) {
|
|
const key = "rotate-fl/" + definition.getHash();
|
|
if (this.operationCache[key]) {
|
|
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
|
|
}
|
|
|
|
const rotated = definition.cloneRotate180();
|
|
|
|
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
|
|
rotated
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Generates a definition for stacking the upper definition onto the lower one
|
|
* @param {ShapeDefinition} lowerDefinition
|
|
* @param {ShapeDefinition} upperDefinition
|
|
* @returns {ShapeDefinition}
|
|
*/
|
|
shapeActionStack(lowerDefinition, upperDefinition) {
|
|
const key = "stack/" + lowerDefinition.getHash() + "/" + upperDefinition.getHash();
|
|
if (this.operationCache[key]) {
|
|
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
|
|
}
|
|
|
|
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.stackShape, null);
|
|
|
|
const stacked = lowerDefinition.cloneAndStackWith(upperDefinition);
|
|
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
|
|
stacked
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Generates a definition for painting it with the given color
|
|
* @param {ShapeDefinition} definition
|
|
* @param {enumColors} color
|
|
* @returns {ShapeDefinition}
|
|
*/
|
|
shapeActionPaintWith(definition, color) {
|
|
const key = "paint/" + definition.getHash() + "/" + color;
|
|
if (this.operationCache[key]) {
|
|
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
|
|
}
|
|
|
|
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.paintShape, null);
|
|
|
|
const colorized = definition.cloneAndPaintWith(color);
|
|
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
|
|
colorized
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Generates a definition for painting it with the 4 colors
|
|
* @param {ShapeDefinition} definition
|
|
* @param {[enumColors, enumColors, enumColors, enumColors]} colors
|
|
* @returns {ShapeDefinition}
|
|
*/
|
|
shapeActionPaintWith4Colors(definition, colors) {
|
|
const key = "paint4/" + definition.getHash() + "/" + colors.join(",");
|
|
if (this.operationCache[key]) {
|
|
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
|
|
}
|
|
const colorized = definition.cloneAndPaintWith4Colors(colors);
|
|
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
|
|
colorized
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Checks if we already have cached this definition, and if so throws it away and returns the already
|
|
* cached variant
|
|
* @param {ShapeDefinition} definition
|
|
*/
|
|
registerOrReturnHandle(definition) {
|
|
const id = definition.getHash();
|
|
if (this.shapeKeyToDefinition[id]) {
|
|
return this.shapeKeyToDefinition[id];
|
|
}
|
|
this.shapeKeyToDefinition[id] = definition;
|
|
// logger.log("Registered shape with key (2)", id);
|
|
return definition;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @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 })
|
|
));
|
|
|
|
return this.registerOrReturnHandle(new ShapeDefinition({ layers: [shapeLayer] }));
|
|
}
|
|
}
|