From 3b4417ba0bd7dc77d3b98e18287f34686d222634 Mon Sep 17 00:00:00 2001 From: Exund Date: Wed, 9 Sep 2020 02:30:35 +0200 Subject: [PATCH] GeoZ base Working Buildings base --- src/js/GeoZ/main.js | 237 ++++++++++++ src/js/GeoZ/mod_building.js | 138 +++++++ src/js/GeoZ/mod_component.js | 13 + src/js/GeoZ/mod_item.js | 5 + src/js/GeoZ/mod_processor.js | 51 +++ src/js/GeoZ/mod_system.js | 121 ++++++ src/js/GeoZ/mod_utils.js | 16 + src/js/GeoZ/mods/test/test.mod.js | 51 +++ src/js/game/systems/item_processor.js | 529 +++++++++++++------------- src/js/main.js | 4 +- 10 files changed, 908 insertions(+), 257 deletions(-) create mode 100644 src/js/GeoZ/main.js create mode 100644 src/js/GeoZ/mod_building.js create mode 100644 src/js/GeoZ/mod_component.js create mode 100644 src/js/GeoZ/mod_item.js create mode 100644 src/js/GeoZ/mod_processor.js create mode 100644 src/js/GeoZ/mod_system.js create mode 100644 src/js/GeoZ/mod_utils.js create mode 100644 src/js/GeoZ/mods/test/test.mod.js diff --git a/src/js/GeoZ/main.js b/src/js/GeoZ/main.js new file mode 100644 index 00000000..9a39daff --- /dev/null +++ b/src/js/GeoZ/main.js @@ -0,0 +1,237 @@ +import { MetaModBuilding } from "./mod_building"; +import { ModComponent } from "./mod_component"; +import { ModItem } from "./mod_item"; +import { ModProcessor } from "./mod_processor"; +import { ModSystem, ModSystemWithFilter } from "./mod_system"; +import { gComponentRegistry, gItemRegistry, gMetaBuildingRegistry } from "../core/global_registries"; +import { GameSystemManager } from "../game/game_system_manager"; +import { GameCore } from "../game/core"; +import { createLogger } from "../core/logging"; +import { registerBuildingVariant } from "../game/building_codes"; +import { supportedBuildings } from "../game/hud/parts/buildings_toolbar"; +import { KEYMAPPINGS, key } from "../game/key_action_mapper"; +import { T } from "../translations"; + +export { MetaModBuilding } from "./mod_building"; +export { ModComponent } from "./mod_component"; +export { ModItem } from "./mod_item"; +export { ModProcessor } from "./mod_processor"; +export { ModSystem, ModSystemWithFilter } from "./mod_system"; + +/** + * @typedef {Object} Mod + * @property {String} name + * @property {Array=} buildings + * @property {Array=} components + * @property {Array=} items + * @property {Array=} processors + * @property {Array=} systems + */ + + const logger = createLogger("GeoZ"); + +/** @type {Array} */ +export const Mods = []; + +/** @type {Array} */ +export const ModComponents = []; + +/** @type {Array} */ +export const ModSystems = []; + +/** @type {Object.} */ +export const ModProcessors = {}; + +/** @type {Array} */ +export const ModItems = []; + +/** @type {Array} */ +export const ModBuildings = []; + +const GameSystemManager_internalInitSystems_original = GameSystemManager.prototype.internalInitSystems; +GameSystemManager.prototype.internalInitSystems = function () { + GameSystemManager_internalInitSystems_original.call(this); + + for (const system of ModSystems) { + //add(system.getId(), system); + const before = system.getUpdateBefore(); + const after = system.getUpdateAfter(); + const system_id = system.getId(); + let override = false; + + if (this.systems[system_id]) { + logger.log( + `⚠️ WARNING ⚠️ A system with the ID "${system_id}" already exists and will be overriden` + ); + override = true; + } + this.systems[system_id] = new system(this.root); + + if (!override) { + if (before) { + const i = this.systemUpdateOrder.indexOf(before); + if (i !== -1) { + this.systemUpdateOrder.splice(i, 0, system_id); + continue; + } + logger.log( + `⚠️ WARNING ⚠️ System "${before}" not found and so system "${system_id}" can't be updated before it` + ); + } + + if (after) { + const i = this.systemUpdateOrder.indexOf(after); + if (i !== -1) { + this.systemUpdateOrder.splice(i + 1, 0, system_id); + continue; + } + logger.log( + `⚠️ WARNING ⚠️ System "${after}" not found and so system "${system_id}" can't be updated after it` + ); + } + } + + if (!this.systemUpdateOrder.includes(system_id)) { + this.systemUpdateOrder.push(system_id); + } + + if (override) { + logger.log(`System "${system_id}" update order : ${this.systemUpdateOrder.indexOf(system_id)}`); + } + } +}; + +// @ts-ignore +const webpack_require = require.context("../", true, /\.js$/); + +const GeoZ = { + Classes: { + MetaModBuilding, + ModComponent, + ModItem, + ModProcessor, + ModSystem, + ModSystemWithFilter, + }, + + require(module) { + return webpack_require(`./${module}.js`); + }, +}; + +export async function initMods() { + const style = "font-size: 35px; font-family: Arial;font-weight: bold; padding: 10px 0;"; + console.log( + `%cGeo%cZ%c modloader\nby %cExund\n`, + `${style} color: #aaa;`, + `${style} color: #7f7;`, + `${style} color: #aaa; font-size: 15px;`, + "color: #ff4300" + ); + + // @ts-ignore + window.GeoZ = GeoZ; + + // @ts-ignore + const local_mods = require.context("./mods", true, /.*\.mod\.js/i); + for (let key of local_mods.keys()) { + let mod = /** @type {Mod} */ (local_mods(key).default); + if (mod.name) { + Mods.push(mod); + } + } + + const local_mods_count = Mods.length; + logger.log(`${local_mods_count} local mods found`); + + /** @type {Array} */ + let external_mods = []; + let storage = localStorage.getItem("mods.external"); + + if (storage) { + external_mods = JSON.parse(storage); + } + + for (const url of external_mods) { + let temp = await fetch(url); + const text = await temp.text(); + const mod = /** @type {Mod} */ (eval(text)); + + if (mod.name) { + Mods.push(mod); + } + } + + const external_mods_count = Mods.length - local_mods_count; + logger.log(`${external_mods_count} external mods found`); + + for (const mod of Mods) { + let mod_infos = `${mod.name} : `; + if (mod.components) { + mod_infos += `${mod.components.length} components, `; + for (const component of mod.components) { + ModComponents.push(component); + gComponentRegistry.register(component); + } + } + + if (mod.systems) { + mod_infos += `${mod.systems.length} systems, `; + for (const system of mod.systems) { + ModSystems.push(system); + } + } + + if (mod.processors) { + mod_infos += `${mod.processors.length} processors, `; + for (const processor of mod.processors) { + ModProcessors[processor.getType()] = processor; + } + } + + if (mod.items) { + mod_infos += `${mod.items.length} items, `; + for (const item of mod.items) { + ModItems.push(item); + gItemRegistry.register(item); + } + } + + if (mod.buildings) { + mod_infos += `${mod.buildings.length} buildings, `; + for (const building of mod.buildings) { + ModBuildings.push(building); + gMetaBuildingRegistry.register(building); + const base_id = building.getId(); + registerBuildingVariant(base_id, building); + + for (const variant of building.getVariants()) { + registerBuildingVariant(`${base_id}-${variant}`, building); + } + + supportedBuildings.push(building); + + KEYMAPPINGS.buildings[base_id] = { keyCode: key(building.getKeybinding()) }; + + const translations = building.getTranslations(); + + T.keybindings.mappings[base_id] = translations.keybinding; + + T.buildings[base_id] = {}; + for (const variant in translations.variants) { + T.buildings[base_id][variant] = translations.variants[variant]; + } + } + } + + logger.log(mod_infos); + } + + for (const categoryId in KEYMAPPINGS) { + for (const mappingId in KEYMAPPINGS[categoryId]) { + KEYMAPPINGS[categoryId][mappingId].id = mappingId; + } + } + + logger.log(`${Mods.length} mods loaded`); +} diff --git a/src/js/GeoZ/mod_building.js b/src/js/GeoZ/mod_building.js new file mode 100644 index 00000000..71ad203e --- /dev/null +++ b/src/js/GeoZ/mod_building.js @@ -0,0 +1,138 @@ +import { MetaBuilding, defaultBuildingVariant } from "../game/meta_building"; +import { AtlasSprite, SpriteAtlasLink } from "../core/sprites"; +import { atlasFiles } from "../core/atlas_definitions"; +import { getFileAsDataURI } from "./mod_utils"; +import { Loader } from "../core/loader"; + +/** + * @typedef {{ + * url: string + * width: number + * height: number + * }} ExternalSpriteMeta + */ + +/** + * @typedef {{ + * name: string, + * description: string + * }} BuildingVariantTranslation + */ + +/** + * @typedef {{ + * variants: {[x: string]: BuildingVariantTranslation, default: BuildingVariantTranslation}, + * keybinding: string + * }} BuildingTranlsations + */ + +export class MetaModBuilding extends MetaBuilding { + /** + * Returns the building IDs + * @returns {String} + */ + static getId() { + abstract; + return ""; + } + + /** + * Returns the building variants IDs + * @returns {Array} + */ + static getVariants() { + return []; + } + + /** + * Returns the building keybinding + * @returns {String} + */ + static getKeybinding() { + abstract; + return ""; + } + + /** + * Returns the building translations + * @returns {BuildingTranlsations} + */ + static getTranslations() { + abstract; + return {variants: { default: { name: "", description: ""} }, keybinding: ""}; + } + + /** + * @param {string} id + */ + constructor(id) { + super(id); + + /** @type {Object} */ + this.cachedSprites = {}; + } + + /** + * Returns the sprite for a given variant + * @param {number} rotationVariant + * @param {string} variant + * @returns {AtlasSprite} + */ + getSprite(rotationVariant, variant) { + const sprite_id = + this.id + (variant === defaultBuildingVariant ? "" : "-" + variant) + "-" + rotationVariant; + + if (this.cachedSprites[sprite_id]) { + return this.cachedSprites[sprite_id]; + } + + const sprite = new AtlasSprite(sprite_id); + + const meta = this.getSpriteMeta(rotationVariant, variant); + const scales = atlasFiles.map(af => af.meta.scale); + for (const res of scales) { + sprite.linksByResolution[res] = Loader.spriteNotFoundSprite.linksByResolution[res]; + } + + getFileAsDataURI(meta.url).then(data => { + const img = document.createElement("img"); + img.src = data; + + const link = new SpriteAtlasLink({ + atlas: img, + packOffsetX: 0, + packOffsetY: 0, + packedX: 0, + packedY: 0, + packedW: meta.width, + packedH: meta.height, + w: meta.width, + h: meta.width, + }); + for (const res of scales) { + sprite.linksByResolution[res] = link; + } + }); + + return sprite; + } + + getBlueprintSprite(rotationVariant = 0, variant = defaultBuildingVariant) { + return this.getSprite(rotationVariant, variant); + } + + getPreviewSprite(rotationVariant = 0, variant = defaultBuildingVariant) { + return this.getSprite(rotationVariant, variant); + } + + /** + * Returns the sprite metadata for a given variant + * @param {number} rotationVariant + * @param {string} variant + * @returns {ExternalSpriteMeta} + */ + getSpriteMeta(rotationVariant, variant) { + abstract; + return { url: "", width: 0, height: 0 }; + } +} diff --git a/src/js/GeoZ/mod_component.js b/src/js/GeoZ/mod_component.js new file mode 100644 index 00000000..4dc0d51a --- /dev/null +++ b/src/js/GeoZ/mod_component.js @@ -0,0 +1,13 @@ +import { Component } from "../game/component"; + +export class ModComponent extends Component { + static getId() { + const className = this.prototype.constructor.name; + let id = className; + const i = className.lastIndexOf("Component"); + if(i !== -1) { + id = id.slice(0, i); + } + return id; + } +} \ No newline at end of file diff --git a/src/js/GeoZ/mod_item.js b/src/js/GeoZ/mod_item.js new file mode 100644 index 00000000..e267459d --- /dev/null +++ b/src/js/GeoZ/mod_item.js @@ -0,0 +1,5 @@ +import { BaseItem } from "../game/base_item"; + +export class ModItem extends BaseItem { + +} \ No newline at end of file diff --git a/src/js/GeoZ/mod_processor.js b/src/js/GeoZ/mod_processor.js new file mode 100644 index 00000000..5173122a --- /dev/null +++ b/src/js/GeoZ/mod_processor.js @@ -0,0 +1,51 @@ +import { Entity } from "../game/entity"; +import { ItemProcessorSystem } from "../game/systems/item_processor"; +import { BaseItem } from "../game/base_item"; + +/** + * @typedef {{ + * items: Array, + * itemsBySlot: Array<{ item: BaseItem, sourceSlot: number }>, + * itemsRaw: Array<{ item: BaseItem, sourceSlot: number }>, + * entity: Entity, + * outItems: Array<{item: BaseItem, requiredSlot?: number, preferredSlot?: number}>, + * system: ItemProcessorSystem + * }} ProcessorParameters +*/ + +export class ModProcessor { + /** + * @returns {String} + */ + static getType() { + return this.prototype.constructor.name; + } + + /** + * @returns {Number} + */ + static getBaseSpeed() { + abstract; + return 0; + } + + /** + * Checks whether it's possible to process something + * @param {Entity} entity + * @returns {Boolean} + */ + static canProcess(entity) { + abstract; + return false; + } + + /** + * Process ther current item + * @param {ProcessorParameters} param0 + * @returns {Boolean} Whether to track the production towards the analytics + */ + static process({}) { + abstract; + return false; + } +} \ No newline at end of file diff --git a/src/js/GeoZ/mod_system.js b/src/js/GeoZ/mod_system.js new file mode 100644 index 00000000..5c6d34ea --- /dev/null +++ b/src/js/GeoZ/mod_system.js @@ -0,0 +1,121 @@ +import { GameSystem } from "../game/game_system"; +import { GameSystemWithFilter } from "../game/game_system_with_filter"; +import { GameRoot } from "../game/root"; +import { Component } from "../game/component"; + +/** + * @typedef { + "belt" + | "itemEjector" + | "mapResources" + | "miner" + | "itemProcessor" + | "undergroundBelt" + | "hub" + | "staticMapEntities" + | "itemAcceptor" + | "storage" + | "wiredPins" + | "beltUnderlays" + | "wire" + | "constantSignal" + | "logicGate" + | "lever" + | "display" + | "itemProcessorOverlays" + | "beltReader" + | "" + * } VanillaSystemId +*/ + +export class ModSystem extends GameSystem { + /** + * @returns {String} Mod system ID + */ + static getId() { + //abstract; + const className = this.prototype.constructor.name; + let id = className; + const i = className.lastIndexOf("System"); + if(i !== -1) { + id = id.slice(0, i); + } + id = id[0].toLowerCase() + id.slice(1); + return id; + } + + /** + * Before which vanilla system should this system update + * @returns {VanillaSystemId} + */ + static getUpdateBefore() { + return ""; + } + + /** + * After which vanilla system should this system update + * @returns {VanillaSystemId} + */ + static getUpdateAfter() { + return ""; + } + + /** + * @param {GameRoot} root + */ + constructor(root) { + super(root); + } +} + +export class ModSystemWithFilter extends GameSystemWithFilter { + /** + * @returns {String} Mod system ID + */ + static getId() { + //abstract; + const className = this.prototype.constructor.name; + let id = className; + const i = className.lastIndexOf("System"); + if(i !== -1) { + id = id.slice(0, i); + } + id = id[0].toLowerCase() + id.slice(1); + return id; + } + + /** + * Before which vanilla system should this system update + * @returns {VanillaSystemId} + */ + static getUpdateBefore() { + return ""; + } + + /** + * After which vanilla system should this system update + * @returns {VanillaSystemId} + */ + static getUpdateAfter() { + return ""; + } + + /** + * @returns {Array} + */ + static getRequiredComponents() { + abstract; + return []; + } + + /** + * Constructs a new game system with the given component filter. It will process + * all entities which have *all* of the passed components + * @param {GameRoot} root + */ + constructor(root) { + super(root, []); + this.requiredComponents = /** @type {Array} */ (Object.getPrototypeOf(this).getRequiredComponents()); + this.requiredComponentIds = this.requiredComponents.map(component => component.getId()); + } +} \ No newline at end of file diff --git a/src/js/GeoZ/mod_utils.js b/src/js/GeoZ/mod_utils.js new file mode 100644 index 00000000..1fe30d89 --- /dev/null +++ b/src/js/GeoZ/mod_utils.js @@ -0,0 +1,16 @@ +/** + * Returns a file as a data URI + * @param {string} url + * @returns {Promise} + */ +export function getFileAsDataURI(url) { + return fetch(url) + .then(response => response.blob()) + .then(blob => { + return new Promise((resolve) => { + var reader = new FileReader() ; + reader.onload = function() { resolve(this.result.toString()) } ; // <--- `this.result` contains a base64 data URI + reader.readAsDataURL(blob) ; + }); + }); +} \ No newline at end of file diff --git a/src/js/GeoZ/mods/test/test.mod.js b/src/js/GeoZ/mods/test/test.mod.js new file mode 100644 index 00000000..7182f0fb --- /dev/null +++ b/src/js/GeoZ/mods/test/test.mod.js @@ -0,0 +1,51 @@ +import * as GeoZ from "../../main"; +import { Vector } from "../../../core/vector"; +import { Entity } from "../../../game/entity"; + +class MetaTestBuilding extends GeoZ.MetaModBuilding { + static getId() { + return "test"; + } + + static getKeybinding() { + return "0"; + } + + static getTranslations() { + return { + variants: { + default: { + name: "Test", + description: "Test GeoZ building" + } + }, + keybinding: "Test" + }; + } + + constructor() { + super("test"); + } + + getSilhouetteColor() { + return "#ff00ff"; + } + + getDimensions() { + return new Vector(1, 1); + } + + getSpriteMeta() { + return {url:"https://raw.githubusercontent.com/Exund/shapez.io/master/res_raw/sprites/wires/boolean_false.png", width: 64, height: 64}; + } + + setupEntityComponents() {} +} + +/**@type {GeoZ.Mod}*/ +const test = { + name: "test", + buildings: [MetaTestBuilding] +}; + +export default test; \ No newline at end of file diff --git a/src/js/game/systems/item_processor.js b/src/js/game/systems/item_processor.js index 2075b566..6fd09865 100644 --- a/src/js/game/systems/item_processor.js +++ b/src/js/game/systems/item_processor.js @@ -251,292 +251,309 @@ export class ItemProcessorSystem extends GameSystemWithFilter { let trackProduction = true; // DO SOME MAGIC + const ModProcessors = require("../../GeoZ/main").ModProcessors; + if (ModProcessors[processorComp.type]) { + trackProduction = ModProcessors[processorComp.type].process({ + items: itemsBySlot.map(e => e.item), + itemsBySlot, + itemsRaw: items, + entity, + outItems, + system: this, + }); + } else { + switch (processorComp.type) { + // SPLITTER + case enumItemProcessorTypes.splitterWires: + case enumItemProcessorTypes.splitter: { + trackProduction = false; + const availableSlots = entity.components.ItemEjector.slots.length; - switch (processorComp.type) { - // SPLITTER - case enumItemProcessorTypes.splitterWires: - case enumItemProcessorTypes.splitter: { - trackProduction = false; - const availableSlots = entity.components.ItemEjector.slots.length; - - let nextSlot = processorComp.nextOutputSlot++ % availableSlots; - for (let i = 0; i < items.length; ++i) { - outItems.push({ - item: items[i].item, - preferredSlot: (nextSlot + i) % availableSlots, - }); - } - break; - } - - // CUTTER - case enumItemProcessorTypes.cutter: { - const inputItem = /** @type {ShapeItem} */ (items[0].item); - assert(inputItem instanceof ShapeItem, "Input for cut is not a shape"); - const inputDefinition = inputItem.definition; - - const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutHalf(inputDefinition); - - for (let i = 0; i < cutDefinitions.length; ++i) { - const definition = cutDefinitions[i]; - if (!definition.isEntirelyEmpty()) { + let nextSlot = processorComp.nextOutputSlot++ % availableSlots; + for (let i = 0; i < items.length; ++i) { outItems.push({ - item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition), - requiredSlot: i, + item: items[i].item, + preferredSlot: (nextSlot + i) % availableSlots, }); } + break; } - break; - } + // CUTTER + case enumItemProcessorTypes.cutter: { + const inputItem = /** @type {ShapeItem} */ (items[0].item); + assert(inputItem instanceof ShapeItem, "Input for cut is not a shape"); + const inputDefinition = inputItem.definition; - // CUTTER (Quad) - case enumItemProcessorTypes.cutterQuad: { - const inputItem = /** @type {ShapeItem} */ (items[0].item); - assert(inputItem instanceof ShapeItem, "Input for cut is not a shape"); - const inputDefinition = inputItem.definition; + const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutHalf(inputDefinition); - const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutQuad(inputDefinition); - - for (let i = 0; i < cutDefinitions.length; ++i) { - const definition = cutDefinitions[i]; - if (!definition.isEntirelyEmpty()) { - outItems.push({ - item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition), - requiredSlot: i, - }); + for (let i = 0; i < cutDefinitions.length; ++i) { + const definition = cutDefinitions[i]; + if (!definition.isEntirelyEmpty()) { + outItems.push({ + item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition), + requiredSlot: i, + }); + } } + + break; } - break; - } + // CUTTER (Quad) + case enumItemProcessorTypes.cutterQuad: { + const inputItem = /** @type {ShapeItem} */ (items[0].item); + assert(inputItem instanceof ShapeItem, "Input for cut is not a shape"); + const inputDefinition = inputItem.definition; - // ROTATER - case enumItemProcessorTypes.rotater: { - const inputItem = /** @type {ShapeItem} */ (items[0].item); - assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape"); - const inputDefinition = inputItem.definition; + const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutQuad(inputDefinition); - const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(inputDefinition); - outItems.push({ - item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition), - }); - break; - } - - // ROTATER (CCW) - case enumItemProcessorTypes.rotaterCCW: { - const inputItem = /** @type {ShapeItem} */ (items[0].item); - assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape"); - const inputDefinition = inputItem.definition; - - const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCCW(inputDefinition); - outItems.push({ - item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition), - }); - break; - } - - // ROTATER (FL) - case enumItemProcessorTypes.rotaterFL: { - const inputItem = /** @type {ShapeItem} */ (items[0].item); - assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape"); - const inputDefinition = inputItem.definition; - - const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateFL(inputDefinition); - outItems.push({ - item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition), - }); - break; - } - - // STACKER - - case enumItemProcessorTypes.stacker: { - const lowerItem = /** @type {ShapeItem} */ (itemsBySlot[0].item); - const upperItem = /** @type {ShapeItem} */ (itemsBySlot[1].item); - - assert(lowerItem instanceof ShapeItem, "Input for lower stack is not a shape"); - assert(upperItem instanceof ShapeItem, "Input for upper stack is not a shape"); - - const stackedDefinition = this.root.shapeDefinitionMgr.shapeActionStack( - lowerItem.definition, - upperItem.definition - ); - outItems.push({ - item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(stackedDefinition), - }); - break; - } - - // TRASH - - case enumItemProcessorTypes.trash: { - // Well this one is easy .. simply do nothing with the item - break; - } - - // MIXER - - case enumItemProcessorTypes.mixer: { - // Find both colors and combine them - const item1 = /** @type {ColorItem} */ (items[0].item); - const item2 = /** @type {ColorItem} */ (items[1].item); - assert(item1 instanceof ColorItem, "Input for color mixer is not a color"); - assert(item2 instanceof ColorItem, "Input for color mixer is not a color"); - - const color1 = item1.color; - const color2 = item2.color; - - // Try finding mixer color, and if we can't mix it we simply return the same color - const mixedColor = enumColorMixingResults[color1][color2]; - let resultColor = color1; - if (mixedColor) { - resultColor = mixedColor; - } - outItems.push({ - item: COLOR_ITEM_SINGLETONS[resultColor], - }); - - break; - } - - // PAINTER - - case enumItemProcessorTypes.painter: { - const shapeItem = /** @type {ShapeItem} */ (itemsBySlot[0].item); - const colorItem = /** @type {ColorItem} */ (itemsBySlot[1].item); - - const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith( - shapeItem.definition, - colorItem.color - ); - - outItems.push({ - item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition), - }); - - break; - } - - // PAINTER (DOUBLE) - - case enumItemProcessorTypes.painterDouble: { - const shapeItem1 = /** @type {ShapeItem} */ (itemsBySlot[0].item); - const shapeItem2 = /** @type {ShapeItem} */ (itemsBySlot[1].item); - const colorItem = /** @type {ColorItem} */ (itemsBySlot[2].item); - - assert(shapeItem1 instanceof ShapeItem, "Input for painter is not a shape"); - assert(shapeItem2 instanceof ShapeItem, "Input for painter is not a shape"); - assert(colorItem instanceof ColorItem, "Input for painter is not a color"); - - const colorizedDefinition1 = this.root.shapeDefinitionMgr.shapeActionPaintWith( - shapeItem1.definition, - colorItem.color - ); - - const colorizedDefinition2 = this.root.shapeDefinitionMgr.shapeActionPaintWith( - shapeItem2.definition, - colorItem.color - ); - outItems.push({ - item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition1), - }); - - outItems.push({ - item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition2), - }); - - break; - } - - // PAINTER (QUAD) - - case enumItemProcessorTypes.painterQuad: { - const shapeItem = /** @type {ShapeItem} */ (itemsBySlot[0].item); - assert(shapeItem instanceof ShapeItem, "Input for painter is not a shape"); - - /** @type {Array} */ - const colors = [null, null, null, null]; - for (let i = 0; i < 4; ++i) { - if (itemsBySlot[i + 1]) { - colors[i] = /** @type {ColorItem} */ (itemsBySlot[i + 1].item).color; + for (let i = 0; i < cutDefinitions.length; ++i) { + const definition = cutDefinitions[i]; + if (!definition.isEntirelyEmpty()) { + outItems.push({ + item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition), + requiredSlot: i, + }); + } } + + break; } - const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith4Colors( - shapeItem.definition, - /** @type {[string, string, string, string]} */ (colors) - ); + // ROTATER + case enumItemProcessorTypes.rotater: { + const inputItem = /** @type {ShapeItem} */ (items[0].item); + assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape"); + const inputDefinition = inputItem.definition; - outItems.push({ - item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition), - }); - break; - } - - // FILTER - case enumItemProcessorTypes.filter: { - // TODO - trackProduction = false; - - const item = itemsBySlot[0].item; - - const network = entity.components.WiredPins.slots[0].linkedNetwork; - if (!network || !network.currentValue) { + const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW( + inputDefinition + ); outItems.push({ - item, - requiredSlot: 1, + item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition), }); break; } - const value = network.currentValue; - if (value.equals(BOOL_TRUE_SINGLETON) || value.equals(item)) { + // ROTATER (CCW) + case enumItemProcessorTypes.rotaterCCW: { + const inputItem = /** @type {ShapeItem} */ (items[0].item); + assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape"); + const inputDefinition = inputItem.definition; + + const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCCW( + inputDefinition + ); outItems.push({ - item, - requiredSlot: 0, - }); - } else { - outItems.push({ - item, - requiredSlot: 1, + item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition), }); + break; } - break; - } + // ROTATER (FL) + case enumItemProcessorTypes.rotaterFL: { + const inputItem = /** @type {ShapeItem} */ (items[0].item); + assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape"); + const inputDefinition = inputItem.definition; - // READER - case enumItemProcessorTypes.reader: { - // Pass through the item - const item = itemsBySlot[0].item; - outItems.push({ item }); - - // Track the item - const readerComp = entity.components.BeltReader; - readerComp.lastItemTimes.push(this.root.time.now()); - readerComp.lastItem = item; - break; - } - - // HUB - case enumItemProcessorTypes.hub: { - trackProduction = false; - - const hubComponent = entity.components.Hub; - assert(hubComponent, "Hub item processor has no hub component"); - - for (let i = 0; i < items.length; ++i) { - const item = /** @type {ShapeItem} */ (items[i].item); - this.root.hubGoals.handleDefinitionDelivered(item.definition); + const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateFL( + inputDefinition + ); + outItems.push({ + item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition), + }); + break; } - break; - } + // STACKER - default: - assertAlways(false, "Unkown item processor type: " + processorComp.type); + case enumItemProcessorTypes.stacker: { + const lowerItem = /** @type {ShapeItem} */ (itemsBySlot[0].item); + const upperItem = /** @type {ShapeItem} */ (itemsBySlot[1].item); + + assert(lowerItem instanceof ShapeItem, "Input for lower stack is not a shape"); + assert(upperItem instanceof ShapeItem, "Input for upper stack is not a shape"); + + const stackedDefinition = this.root.shapeDefinitionMgr.shapeActionStack( + lowerItem.definition, + upperItem.definition + ); + outItems.push({ + item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(stackedDefinition), + }); + break; + } + + // TRASH + + case enumItemProcessorTypes.trash: { + // Well this one is easy .. simply do nothing with the item + break; + } + + // MIXER + + case enumItemProcessorTypes.mixer: { + // Find both colors and combine them + const item1 = /** @type {ColorItem} */ (items[0].item); + const item2 = /** @type {ColorItem} */ (items[1].item); + assert(item1 instanceof ColorItem, "Input for color mixer is not a color"); + assert(item2 instanceof ColorItem, "Input for color mixer is not a color"); + + const color1 = item1.color; + const color2 = item2.color; + + // Try finding mixer color, and if we can't mix it we simply return the same color + const mixedColor = enumColorMixingResults[color1][color2]; + let resultColor = color1; + if (mixedColor) { + resultColor = mixedColor; + } + outItems.push({ + item: COLOR_ITEM_SINGLETONS[resultColor], + }); + + break; + } + + // PAINTER + + case enumItemProcessorTypes.painter: { + const shapeItem = /** @type {ShapeItem} */ (itemsBySlot[0].item); + const colorItem = /** @type {ColorItem} */ (itemsBySlot[1].item); + + const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith( + shapeItem.definition, + colorItem.color + ); + + outItems.push({ + item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition), + }); + + break; + } + + // PAINTER (DOUBLE) + + case enumItemProcessorTypes.painterDouble: { + const shapeItem1 = /** @type {ShapeItem} */ (itemsBySlot[0].item); + const shapeItem2 = /** @type {ShapeItem} */ (itemsBySlot[1].item); + const colorItem = /** @type {ColorItem} */ (itemsBySlot[2].item); + + assert(shapeItem1 instanceof ShapeItem, "Input for painter is not a shape"); + assert(shapeItem2 instanceof ShapeItem, "Input for painter is not a shape"); + assert(colorItem instanceof ColorItem, "Input for painter is not a color"); + + const colorizedDefinition1 = this.root.shapeDefinitionMgr.shapeActionPaintWith( + shapeItem1.definition, + colorItem.color + ); + + const colorizedDefinition2 = this.root.shapeDefinitionMgr.shapeActionPaintWith( + shapeItem2.definition, + colorItem.color + ); + outItems.push({ + item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition1), + }); + + outItems.push({ + item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition2), + }); + + break; + } + + // PAINTER (QUAD) + + case enumItemProcessorTypes.painterQuad: { + const shapeItem = /** @type {ShapeItem} */ (itemsBySlot[0].item); + assert(shapeItem instanceof ShapeItem, "Input for painter is not a shape"); + + /** @type {Array} */ + const colors = [null, null, null, null]; + for (let i = 0; i < 4; ++i) { + if (itemsBySlot[i + 1]) { + colors[i] = /** @type {ColorItem} */ (itemsBySlot[i + 1].item).color; + } + } + + const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith4Colors( + shapeItem.definition, + /** @type {[string, string, string, string]} */ (colors) + ); + + outItems.push({ + item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition), + }); + break; + } + + // FILTER + case enumItemProcessorTypes.filter: { + // TODO + trackProduction = false; + + const item = itemsBySlot[0].item; + + const network = entity.components.WiredPins.slots[0].linkedNetwork; + if (!network || !network.currentValue) { + outItems.push({ + item, + requiredSlot: 1, + }); + break; + } + + const value = network.currentValue; + if (value.equals(BOOL_TRUE_SINGLETON) || value.equals(item)) { + outItems.push({ + item, + requiredSlot: 0, + }); + } else { + outItems.push({ + item, + requiredSlot: 1, + }); + } + + break; + } + + // READER + case enumItemProcessorTypes.reader: { + // Pass through the item + const item = itemsBySlot[0].item; + outItems.push({ item }); + + // Track the item + const readerComp = entity.components.BeltReader; + readerComp.lastItemTimes.push(this.root.time.now()); + readerComp.lastItem = item; + break; + } + + // HUB + case enumItemProcessorTypes.hub: { + trackProduction = false; + + const hubComponent = entity.components.Hub; + assert(hubComponent, "Hub item processor has no hub component"); + + for (let i = 0; i < items.length; ++i) { + const item = /** @type {ShapeItem} */ (items[i].item); + this.root.hubGoals.handleDefinitionDelivered(item.definition); + } + + break; + } + + default: + assertAlways(false, "Unkown item processor type: " + processorComp.type); + } } // Track produced items diff --git a/src/js/main.js b/src/js/main.js index 5b9df699..48f3ff26 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -10,6 +10,7 @@ import { initDrawUtils } from "./core/draw_utils"; import { initItemRegistry } from "./game/item_registry"; import { initMetaBuildingRegistry } from "./game/meta_building_registry"; import { initGameSpeedRegistry } from "./game/game_speed_registry"; +import { initMods } from "./GeoZ/main"; const logger = createLogger("main"); @@ -77,6 +78,7 @@ console.log("%cDEVCODE BUILT IN", "color: #f77"); logSection("Boot Process", "#f9a825"); +initMods(); initDrawUtils(); initComponentRegistry(); initItemRegistry(); @@ -91,4 +93,4 @@ function bootApp() { app.boot(); } -window.addEventListener("load", bootApp); +window.addEventListener("load", bootApp); \ No newline at end of file