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

Merge branch 'GeoZ' into component-editor

This commit is contained in:
dengr1065 2020-09-17 15:14:41 +03:00 committed by GitHub
commit 94f32ad226
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 2186 additions and 730 deletions

View File

@ -163,7 +163,7 @@ function serve({ standalone }) {
}); });
// Watch .scss files, those trigger a css rebuild // Watch .scss files, those trigger a css rebuild
gulp.watch(["../src/**/*.scss"], gulp.series("css.dev")); gulp.watch(["../src/**/*.scss"], { usePolling: true }, gulp.series("css.dev"));
// Watch .html files, those trigger a html rebuild // Watch .html files, those trigger a html rebuild
gulp.watch("../src/**/*.html", gulp.series(standalone ? "html.standalone-dev" : "html.dev")); gulp.watch("../src/**/*.html", gulp.series(standalone ? "html.standalone-dev" : "html.dev"));
@ -172,7 +172,7 @@ function serve({ standalone }) {
// gulp.watch(["../res_raw/sounds/**/*.mp3", "../res_raw/sounds/**/*.wav"], gulp.series("sounds.dev")); // gulp.watch(["../res_raw/sounds/**/*.mp3", "../res_raw/sounds/**/*.wav"], gulp.series("sounds.dev"));
// Watch translations // Watch translations
gulp.watch("../translations/**/*.yaml", gulp.series("translations.convertToJson")); gulp.watch("../translations/**/*.yaml", { usePolling: true }, gulp.series("translations.convertToJson"));
gulp.watch( gulp.watch(
["../res_raw/sounds/sfx/*.mp3", "../res_raw/sounds/sfx/*.wav"], ["../res_raw/sounds/sfx/*.mp3", "../res_raw/sounds/sfx/*.wav"],

View File

@ -14,6 +14,9 @@ module.exports = ({ watch = false, standalone = false }) => {
"bundle.js": [path.resolve(__dirname, "../src/js/main.js")], "bundle.js": [path.resolve(__dirname, "../src/js/main.js")],
}, },
watch, watch,
watchOptions: {
poll: 1000
},
node: { node: {
fs: "empty", fs: "empty",
}, },

View File

@ -1,5 +1,12 @@
#state_KeybindingsState { #state_SettingsState {
$colorCategoryButton: #eee;
$colorCategoryButtonSelected: #5f748b;
.content { .content {
display: flex;
overflow-y: scroll;
.topEntries { .topEntries {
display: grid; display: grid;
grid-template-columns: 1fr auto; grid-template-columns: 1fr auto;
@ -50,6 +57,148 @@
} }
} }
} }
.categoryContainer {
width: 100%;
.category {
display: none;
&.active {
display: block;
}
.setting {
@include S(padding, 10px);
background: #eeeff5;
@include S(border-radius, $globalBorderRadius);
@include S(margin-bottom, 5px);
label {
text-transform: uppercase;
@include Text;
}
.desc {
@include S(margin-top, 5px);
@include SuperSmallText;
color: #aaadb2;
}
> .row {
display: grid;
align-items: center;
grid-template-columns: 1fr auto;
}
&.disabled {
// opacity: 0.3;
pointer-events: none;
* {
pointer-events: none !important;
cursor: default !important;
}
position: relative;
.standaloneOnlyHint {
@include PlainText;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: all;
display: flex;
align-items: center;
justify-content: center;
background: rgba(#fff, 0.5);
text-transform: uppercase;
color: $colorRedBright;
}
}
.value.enum {
background: #fff;
@include PlainText;
display: flex;
align-items: flex-start;
pointer-events: all;
cursor: pointer;
justify-content: center;
@include S(min-width, 100px);
@include S(border-radius, $globalBorderRadius);
@include S(padding, 4px);
@include S(padding-right, 15px);
background: #fff uiResource("icons/enum_selector.png") calc(100% - #{D(5px)})
calc(50% + #{D(1px)}) / #{D(15px)} no-repeat;
transition: background-color 0.12s ease-in-out;
&:hover {
background-color: #fafafa;
}
}
}
}
}
.sidebar {
display: flex;
flex-direction: column;
@include S(min-width, 210px);
@include S(max-width, 320px);
width: 30%;
height: 100%;
position: sticky;
top: 0;
@include S(margin-left, 20px);
@include S(margin-right, 32px);
.other {
margin-top: auto;
}
button {
@include S(margin-top, 4px);
width: calc(100% - #{D(20px)});
text-align: start;
&::after {
content: unset;
}
}
button.categoryButton,
button.about {
background-color: $colorCategoryButton;
color: #777a7f;
&.active {
background-color: $colorCategoryButtonSelected;
color: #fff;
&:hover {
opacity: 1;
}
}
&.pressed {
transform: none !important;
}
}
.versionbar {
@include S(margin-top, 20px);
@include SuperSmallText;
display: grid;
align-items: center;
grid-template-columns: 1fr auto;
.buildVersion {
display: flex;
flex-direction: column;
color: #aaadaf;
}
}
}
} }
@include DarkThemeOverride { @include DarkThemeOverride {

View File

@ -164,24 +164,28 @@
position: relative; position: relative;
.updateLabel { .updateLabel {
background-color: $mainBgColor;
border-radius: 999em;
padding: 0px 10px;
position: absolute; position: absolute;
transform: translateX(50%) rotate(-5deg); transform: translateX(50%) rotate(-5deg) scale(1.5);
color: $colorRedBright; color: #7d808a;
//color: $colorRedBright;
@include Heading; @include Heading;
text-transform: uppercase; //text-transform: uppercase;
font-weight: bold; font-weight: bold;
@include S(right, 40px); @include S(right, 40px);
@include S(bottom, 20px); @include S(bottom, 20px);
@include InlineAnimation(1.3s ease-in-out infinite) { @include InlineAnimation(1.3s ease-in-out infinite) {
50% { 50% {
transform: translateX(50%) rotate(-7deg) scale(1.1); transform: translateX(50%) rotate(-7deg) scale(1.65);
} }
} }
@include DarkThemeOverride { /*@include DarkThemeOverride {
color: $colorBlueBright; color: $colorBlueBright;
} }*/
} }
} }

221
src/js/GeoZ/main.js Normal file
View File

@ -0,0 +1,221 @@
//Mod imports
import { MetaModBuilding } from "./mod_building";
import { ModComponent } from "./mod_component";
import { ModItem } from "./mod_item";
import { ModProcessor } from "./mod_processor";
import { ModWireProcessor } from "./mod_wireprocessor";
import { ModSystem, ModSystemWithFilter } from "./mod_system";
import { keyCodeOf } from "./mod_utils";
//Game imports
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";
import { ShapeData, allShapeData, initShapes } from "../game/shapes";
import { globalConfig } from "../core/config";
export { MetaModBuilding } from "./mod_building";
export { ModComponent } from "./mod_component";
export { ModItem } from "./mod_item";
export { ModProcessor } from "./mod_processor";
export { ModWireProcessor } from "./mod_wireprocessor";
export { ModSystem, ModSystemWithFilter } from "./mod_system";
/**
* @typedef {Object} Mod
* @property {String} name
* @property {Array<typeof MetaModBuilding>=} buildings
* @property {Array<typeof ModComponent>=} components
* @property {Array<typeof ModItem>=} items
* @property {Array<typeof ModProcessor>=} processors
* @property {Array<typeof ModWireProcessor>=} wireProcessors
* @property {Array<typeof ModSystem | typeof ModSystemWithFilter>=} systems
* @property {Array<ShapeData>=} shapes
*/
export const logger = createLogger("GeoZ");
/** @type {Array<Mod>} */
export const Mods = [];
/** @type {Array<typeof ModComponent>} */
export const ModComponents = [];
/** @type {Array<typeof ModSystem | typeof ModSystemWithFilter>} */
export const ModSystems = [];
/** @type {Object.<string, typeof ModProcessor>} */
export const ModProcessors = {};
/** @type {Object.<string, typeof ModWireProcessor>} */
export const ModWireProcessors = {};
/** @type {Array<typeof ModItem>} */
export const ModItems = [];
/** @type {Array<typeof MetaModBuilding>} */
export const ModBuildings = [];
/** @type {Array<ShapeData>} */
export const ModShapes = [];
// @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<string>} */
let external_mods = [];
let storage = localStorage.getItem("mods.external");
if (storage) {
external_mods = JSON.parse(storage);
}
for (const url of external_mods) {
try {
let temp = await fetch(url);
const text = await temp.text();
const mod = /** @type {Mod} */ (eval(text));
if (mod.name) {
Mods.push(mod);
}
} catch {
logger.log(`🛑 Failed to load mod at : ${url}`);
}
}
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) {
const type = processor.getType();
ModProcessors[type] = processor;
globalConfig.buildingSpeeds[type] = processor.getBaseSpeed();
}
}
if (mod.wireProcessors) {
mod_infos += `${mod.wireProcessors.length} wire processors, `;
for (const wireProcessor of mod.wireProcessors) {
const type = wireProcessor.getType();
ModWireProcessors[type] = wireProcessor;
}
}
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, variant);
}
supportedBuildings.push(building);
KEYMAPPINGS.buildings[base_id] = {
keyCode: keyCodeOf(building.getKeybinding()),
id: base_id,
};
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];
}
}
}
if (mod.shapes) {
mod_infos += `${mod.shapes.length} shapes, `;
for (const shape of mod.shapes) {
ModShapes.push(shape);
allShapeData[shape.id] = shape;
}
}
logger.log(mod_infos);
}
initShapes();
logger.log(`${Mods.length} mods loaded`);
}

157
src/js/GeoZ/mod_building.js Normal file
View File

@ -0,0 +1,157 @@
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 {{
* normal: ExternalSpriteMeta
* blueprint: ExternalSpriteMeta
* }} SpriteTypesMetas
*/
/**
* @typedef {{
* default: Array<SpriteTypesMetas>
* [variant: string]: Array<SpriteTypesMetas>
* }} BuildingSpriteMetas
*/
/**
* @typedef {{
* name: string,
* description: string
* }} BuildingVariantTranslation
*/
/**
* @typedef {{
* variants: {[variant: 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<String>}
*/
static getVariants() {
return [];
}
/**
* Returns the building keybinding
* @returns {String | number}
*/
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<string, AtlasSprite>} */
this.cachedSprites = {};
}
/**
* Returns the sprite for a given variant
* @param {number} rotationVariant
* @param {string} variant
* @param {keyof BuildingSpriteMetas} type
* @returns {AtlasSprite}
*/
getSprite(rotationVariant, variant, type = "normal") {
const sprite_id =
this.id +
(variant === defaultBuildingVariant ? "" : "-" + variant) +
"-" +
rotationVariant +
"-" +
type;
if (this.cachedSprites[sprite_id]) {
return this.cachedSprites[sprite_id];
}
const sprite = new AtlasSprite(sprite_id);
this.cachedSprites[sprite_id] = sprite;
const meta = this.getSpriteMetas()[variant][rotationVariant][type];
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, "blueprint");
}
getPreviewSprite(rotationVariant = 0, variant = defaultBuildingVariant) {
return this.getSprite(rotationVariant, variant);
}
/**
* Returns the sprite metadata for a given variant
* @returns {BuildingSpriteMetas}
*/
getSpriteMetas() {
abstract;
return null;
}
}

View File

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

5
src/js/GeoZ/mod_item.js Normal file
View File

@ -0,0 +1,5 @@
import { BaseItem } from "../game/base_item";
export class ModItem extends BaseItem {
}

View File

@ -0,0 +1,50 @@
import { Entity } from "../game/entity";
import { ItemProcessorSystem } from "../game/systems/item_processor";
import { BaseItem } from "../game/base_item";
/**
* @typedef {{
* items: Array<BaseItem>,
* 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) {
return true;
}
/**
* Process ther current item
* @param {ProcessorParameters} param0
* @returns {Boolean} Whether to track the production towards the analytics
*/
static process({}) {
abstract;
return false;
}
}

121
src/js/GeoZ/mod_system.js Normal file
View File

@ -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<typeof Component>}
*/
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<typeof Component>} */ (Object.getPrototypeOf(this).getRequiredComponents());
this.requiredComponentIds = this.requiredComponents.map(component => component.getId());
}
}

30
src/js/GeoZ/mod_utils.js Normal file
View File

@ -0,0 +1,30 @@
/**
* Returns a file as a data URI
* @param {string} url
* @returns {Promise<String>}
*/
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) ;
});
});
}
/**
*
* @param {number | string} key
*/
export function keyCodeOf(key) {
if (typeof key === "number") {
return key;
}
if (key.match(/F\d+/)) {
return 111 + +key.slice(1);
}
return key.toUpperCase().charCodeAt(0);
}

View File

@ -0,0 +1,24 @@
import { BaseItem } from "../game/base_item";
import { LogicGateSystem } from "../game/systems/logic_gate";
/**
* Custom wire processor (logic gate/virtual processor)
*/
export class ModWireProcessor {
/**
* @returns {String}
*/
static getType() {
return this.prototype.constructor.name;
}
/**
* @param {Array<BaseItem|null>} parameters
* @param {LogicGateSystem} system
* @returns {Array<BaseItem>|BaseItem}
*/
static compute(system, parameters) {
abstract;
return [];
}
}

View File

@ -0,0 +1,362 @@
import * as GeoZ from "../../main";
import { Vector, enumDirection } from "../../../core/vector";
import { Entity } from "../../../game/entity";
import { ModProcessor, ProcessorParameters } from "../../mod_processor";
import { ShapeItem } from "../../../game/items/shape_item";
import { ShapeDefinition } from "../../../game/shape_definition";
import { ItemProcessorComponent } from "../../../game/components/item_processor";
import { ItemEjectorComponent } from "../../../game/components/item_ejector";
import { ItemAcceptorComponent } from "../../../game/components/item_acceptor";
import { ModWireProcessor } from "../../mod_wireprocessor";
import { BaseItem } from "../../../game/base_item";
import { LogicGateSystem } from "../../../game/systems/logic_gate";
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON, isTruthyItem } from "../../../game/items/boolean_item";
import { enumPinSlotType, WiredPinsComponent } from "../../../game/components/wired_pins";
import { LogicGateComponent } from "../../../game/components/logic_gate";
import { defaultBuildingVariant } from "../../../game/meta_building";
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);
}
/**
* @returns {import("../../mod_building").BuildingSpriteMetas}
*/
getSpriteMetas() {
const normal = {
url:
"https://raw.githubusercontent.com/Exund/shapez.io/master/res_raw/sprites/wires/boolean_false.png",
width: 64,
height: 64,
};
return {
default: [
{
normal,
blueprint: normal,
},
],
};
}
/**
* Creates the entity at the given location
* @param {Entity} entity
*/
setupEntityComponents(entity) {
entity.addComponent(
new ItemProcessorComponent({
inputsPerCharge: 1,
processorType: SquareConverter.getType(),
})
);
entity.addComponent(
new ItemEjectorComponent({
slots: [{ pos: new Vector(0, 0), direction: enumDirection.top }],
})
);
entity.addComponent(
new ItemAcceptorComponent({
slots: [
{
pos: new Vector(0, 0),
directions: [enumDirection.bottom],
filter: "shape",
},
],
})
);
}
}
class MetaInvertedGatesBuilding extends GeoZ.MetaModBuilding {
static getId() {
return "NANDGate";
}
static getKeybinding() {
return "0";
}
static getTranslations() {
return {
variants: {
default: {
name: "NAND Gate",
description: "Test GeoZ building for custom wire processor",
},
NORGate: {
name: "NOR Gate",
description: "Test GeoZ building for custom wire processor",
},
XNORGate: {
name: "XNOR Gate",
description: "Test GeoZ building for custom wire processor",
},
},
keybinding: "NAND Gate",
};
}
static getVariants() {
return ["NORGate", "XNORGate"];
}
constructor() {
super("NANDGate");
}
getSilhouetteColor() {
return "#89dc60";
}
getDimensions() {
return new Vector(1, 1);
}
getAvailableVariants() {
return [...super.getAvailableVariants(null), ...MetaInvertedGatesBuilding.getVariants()];
}
/**
* @returns {Layer}
*/
getLayer() {
return "wires";
}
/**
* @returns {import("../../mod_building").BuildingSpriteMetas}
*/
getSpriteMetas() {
return {
default: [
{
normal: {
url:
"https://raw.githubusercontent.com/Exund/shapez.io/master/res_raw/sprites/buildings/logic_gate.png",
width: 192,
height: 192,
},
blueprint: {
url:
"https://raw.githubusercontent.com/Exund/shapez.io/master/res_raw/sprites/blueprints/logic_gate.png",
width: 192,
height: 192,
},
},
],
NORGate: [
{
normal: {
url:
"https://raw.githubusercontent.com/Exund/shapez.io/master/res_raw/sprites/buildings/logic_gate-or.png",
width: 192,
height: 192,
},
blueprint: {
url:
"https://raw.githubusercontent.com/Exund/shapez.io/master/res_raw/sprites/blueprints/logic_gate-or.png",
width: 192,
height: 192,
},
},
],
XNORGate: [
{
normal: {
url:
"https://raw.githubusercontent.com/Exund/shapez.io/master/res_raw/sprites/buildings/logic_gate-xor.png",
width: 192,
height: 192,
},
blueprint: {
url:
"https://raw.githubusercontent.com/Exund/shapez.io/master/res_raw/sprites/blueprints/logic_gate-xor.png",
width: 192,
height: 192,
},
},
],
};
}
/**
* @param {Entity} entity
* @param {string} variant
*/
updateVariants(entity, rotationVariant, variant) {
entity.components.LogicGate.type = enumInvertedGatesVariants[variant];
}
/**
* Creates the entity at the given location
* @param {Entity} entity
*/
setupEntityComponents(entity) {
entity.addComponent(
new WiredPinsComponent({
slots: [
{
pos: new Vector(0, 0),
direction: enumDirection.top,
type: enumPinSlotType.logicalEjector,
},
{
pos: new Vector(0, 0),
direction: enumDirection.left,
type: enumPinSlotType.logicalAcceptor,
},
{
pos: new Vector(0, 0),
direction: enumDirection.right,
type: enumPinSlotType.logicalAcceptor,
},
],
})
);
entity.addComponent(new LogicGateComponent({ type: NANDGate.getType() }));
}
}
const enumInvertedGatesVariants = {
[defaultBuildingVariant]: "NANDGate",
};
for (const v of MetaInvertedGatesBuilding.getVariants()) {
enumInvertedGatesVariants[v] = v;
}
class SquareConverter extends ModProcessor {
/**
* @returns {Number}
*/
static getBaseSpeed() {
return 0.5;
}
/**
* Process ther current item
* @param {ProcessorParameters} param0
* @returns {Boolean} Whether to track the production towards the analytics
*/
static process({ outItems }) {
outItems.push({ item: new ShapeItem(ShapeDefinition.fromShortKey("SuSuSuSu")) });
return true;
}
}
class NANDGate extends ModWireProcessor {
/**
* @param {Array<BaseItem|null>} parameters
* @param {LogicGateSystem} system
* @returns {Array<BaseItem>|BaseItem}
*/
static compute(system, parameters) {
assert(parameters.length === 2, "bad parameter count for NAND");
return isTruthyItem(parameters[0]) && isTruthyItem(parameters[1])
? BOOL_FALSE_SINGLETON
: BOOL_TRUE_SINGLETON;
}
}
class NORGate extends ModWireProcessor {
/**
* @param {Array<BaseItem|null>} parameters
* @param {LogicGateSystem} system
* @returns {Array<BaseItem>|BaseItem}
*/
static compute(system, parameters) {
assert(parameters.length === 2, "bad parameter count for NOR");
return isTruthyItem(parameters[0]) || isTruthyItem(parameters[1])
? BOOL_FALSE_SINGLETON
: BOOL_TRUE_SINGLETON;
}
}
class XNORGate extends ModWireProcessor {
/**
* @param {Array<BaseItem|null>} parameters
* @param {LogicGateSystem} system
* @returns {Array<BaseItem>|BaseItem}
*/
static compute(system, parameters) {
assert(parameters.length === 2, "bad parameter count for XNOR");
return isTruthyItem(parameters[0]) !== isTruthyItem(parameters[1])
? BOOL_FALSE_SINGLETON
: BOOL_TRUE_SINGLETON;
}
}
class VirtualStacker extends ModWireProcessor {
/**
* @param {Array<BaseItem|null>} parameters
* @param {LogicGateSystem} system
* @returns {Array<BaseItem>|BaseItem}
*/
static compute(system, parameters) {
const item1 = parameters[0];
const item2 = parameters[0];
if (!item1 || !item2 || item1.getItemType() !== "shape" || item2.getItemType() !== "shape") {
return null;
}
const definition1 = /** @type {ShapeItem} */ (item1).definition;
const definition2 = /** @type {ShapeItem} */ (item2).definition;
const result = system.root.shapeDefinitionMgr.shapeActionStack(definition1, definition2);
return system.root.shapeDefinitionMgr.getShapeItemFromDefinition(result);
}
}
/**@type {GeoZ.Mod}*/
const test = {
name: "test",
buildings: [MetaTestBuilding, MetaInvertedGatesBuilding],
processors: [SquareConverter],
wireProcessors: [NANDGate, NORGate, XNORGate, VirtualStacker],
shapes: [
{
id: "leaf",
code: "F",
draw: "M 0 0 v 0.5 a 0.5 0.5 0 0 0 0.5 0.5 h 0.5 v -0.5 a 0.5 0.5 0 0 0 -0.5 -0.5 z",
tier: 2,
spawnData: {
color: "yellow",
},
},
],
};
export default test;

View File

@ -145,7 +145,7 @@ export class Application {
MobileWarningState, MobileWarningState,
MainMenuState, MainMenuState,
InGameState, InGameState,
SettingsState, //SettingsState,
KeybindingsState, KeybindingsState,
AboutState, AboutState,
ChangelogState, ChangelogState,

View File

@ -1,136 +1,136 @@
import { queryParamOptions } from "./query_parameters"; import { queryParamOptions } from "./query_parameters";
export const IS_DEBUG = export const IS_DEBUG =
G_IS_DEV && G_IS_DEV &&
typeof window !== "undefined" && typeof window !== "undefined" &&
window.location.port === "3005" && window.location.port === "3005" &&
(window.location.host.indexOf("localhost:") >= 0 || window.location.host.indexOf("192.168.0.") >= 0) && (window.location.host.indexOf("localhost:") >= 0 || window.location.host.indexOf("192.168.0.") >= 0) &&
window.location.search.indexOf("nodebug") < 0; window.location.search.indexOf("nodebug") < 0;
export const IS_DEMO = queryParamOptions.fullVersion export const IS_DEMO = queryParamOptions.fullVersion
? false ? false
: (!G_IS_DEV && !G_IS_STANDALONE) || : (!G_IS_DEV && !G_IS_STANDALONE) ||
(typeof window !== "undefined" && window.location.search.indexOf("demo") >= 0); (typeof window !== "undefined" && window.location.search.indexOf("demo") >= 0);
export const SUPPORT_TOUCH = false; export const SUPPORT_TOUCH = false;
const smoothCanvas = true; const smoothCanvas = true;
export const THIRDPARTY_URLS = { export const THIRDPARTY_URLS = {
discord: "https://discord.gg/HN7EVzV", discord: "https://discord.gg/HN7EVzV",
github: "https://github.com/tobspr/shapez.io", github: "https://github.com/tobspr/shapez.io",
reddit: "https://www.reddit.com/r/shapezio", reddit: "https://www.reddit.com/r/shapezio",
standaloneStorePage: "https://store.steampowered.com/app/1318690/shapezio/", standaloneStorePage: "https://store.steampowered.com/app/1318690/shapezio/",
}; };
export const globalConfig = { export const globalConfig = {
// Size of a single tile in Pixels. // Size of a single tile in Pixels.
// NOTICE: Update webpack.production.config too! // NOTICE: Update webpack.production.config too!
tileSize: 32, tileSize: 32,
halfTileSize: 16, halfTileSize: 16,
// Which dpi the assets have // Which dpi the assets have
assetsDpi: 192 / 32, assetsDpi: 192 / 32,
assetsSharpness: 1.5, assetsSharpness: 1.5,
shapesSharpness: 1.4, shapesSharpness: 1.4,
// Production analytics // Production analytics
statisticsGraphDpi: 2.5, statisticsGraphDpi: 2.5,
statisticsGraphSlices: 100, statisticsGraphSlices: 100,
analyticsSliceDurationSeconds: G_IS_DEV ? 1 : 10, analyticsSliceDurationSeconds: G_IS_DEV ? 1 : 10,
minimumTickRate: 25, minimumTickRate: 25,
maximumTickRate: 500, maximumTickRate: 500,
// Map // Map
mapChunkSize: 16, mapChunkSize: 16,
mapChunkOverviewMinZoom: 0.9, mapChunkOverviewMinZoom: 0.9,
mapChunkWorldSize: null, // COMPUTED mapChunkWorldSize: null, // COMPUTED
// Belt speeds // Belt speeds
// NOTICE: Update webpack.production.config too! // NOTICE: Update webpack.production.config too!
beltSpeedItemsPerSecond: 2, beltSpeedItemsPerSecond: 2,
minerSpeedItemsPerSecond: 0, // COMPUTED minerSpeedItemsPerSecond: 0, // COMPUTED
defaultItemDiameter: 20, defaultItemDiameter: 20,
itemSpacingOnBelts: 0.63, itemSpacingOnBelts: 0.63,
wiresSpeedItemsPerSecond: 6, wiresSpeedItemsPerSecond: 6,
undergroundBeltMaxTilesByTier: [5, 9], undergroundBeltMaxTilesByTier: [5, 9],
readerAnalyzeIntervalSeconds: G_IS_DEV ? 3 : 10, readerAnalyzeIntervalSeconds: G_IS_DEV ? 3 : 10,
buildingSpeeds: { buildingSpeeds: {
cutter: 1 / 4, cutter: 1 / 4,
cutterQuad: 1 / 4, cutterQuad: 1 / 4,
rotater: 1 / 1, rotater: 1 / 1,
rotaterCCW: 1 / 1, rotaterCCW: 1 / 1,
rotaterFL: 1 / 1, rotaterFL: 1 / 1,
painter: 1 / 6, painter: 1 / 6,
painterDouble: 1 / 8, painterDouble: 1 / 8,
painterQuad: 1 / 8, painterQuad: 1 / 8,
mixer: 1 / 5, mixer: 1 / 5,
stacker: 1 / 6, stacker: 1 / 6,
}, },
// Zooming // Zooming
initialZoom: 1.9, initialZoom: 1.9,
minZoomLevel: 0.1, minZoomLevel: 0.1,
maxZoomLevel: 3, maxZoomLevel: 3,
// Global game speed // Global game speed
gameSpeed: 1, gameSpeed: 1,
warmupTimeSecondsFast: 0.1, warmupTimeSecondsFast: 0.1,
warmupTimeSecondsRegular: 1, warmupTimeSecondsRegular: 1,
smoothing: { smoothing: {
smoothMainCanvas: smoothCanvas && true, smoothMainCanvas: smoothCanvas && true,
quality: "low", // Low is CRUCIAL for mobile performance! quality: "low", // Low is CRUCIAL for mobile performance!
}, },
rendering: {}, rendering: {},
debug: require("./config.local").default, debug: G_IS_DEV ? require("./config.local").default : {},
// Secret vars // Secret vars
info: { info: {
// Binary file salt // Binary file salt
file: "Ec'])@^+*9zMevK3uMV4432x9%iK'=", file: "Ec'])@^+*9zMevK3uMV4432x9%iK'=",
// Savegame salt // Savegame salt
sgSalt: "}95Q3%8/.837Lqym_BJx%q7)pAHJbF", sgSalt: "}95Q3%8/.837Lqym_BJx%q7)pAHJbF",
// Analytics key // Analytics key
analyticsApiKey: "baf6a50f0cc7dfdec5a0e21c88a1c69a4b34bc4a", analyticsApiKey: "baf6a50f0cc7dfdec5a0e21c88a1c69a4b34bc4a",
}, },
}; };
export const IS_MOBILE = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); export const IS_MOBILE = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
// Automatic calculations // Automatic calculations
globalConfig.minerSpeedItemsPerSecond = globalConfig.beltSpeedItemsPerSecond / 5; globalConfig.minerSpeedItemsPerSecond = globalConfig.beltSpeedItemsPerSecond / 5;
globalConfig.mapChunkWorldSize = globalConfig.mapChunkSize * globalConfig.tileSize; globalConfig.mapChunkWorldSize = globalConfig.mapChunkSize * globalConfig.tileSize;
// Dynamic calculations // Dynamic calculations
if (globalConfig.debug.disableMapOverview) { if (globalConfig.debug.disableMapOverview) {
globalConfig.mapChunkOverviewMinZoom = 0; globalConfig.mapChunkOverviewMinZoom = 0;
} }
// Stuff for making the trailer // Stuff for making the trailer
if (G_IS_DEV && globalConfig.debug.renderForTrailer) { if (G_IS_DEV && globalConfig.debug.renderForTrailer) {
globalConfig.debug.framePausesBetweenTicks = 32; globalConfig.debug.framePausesBetweenTicks = 32;
// globalConfig.mapChunkOverviewMinZoom = 0.0; // globalConfig.mapChunkOverviewMinZoom = 0.0;
// globalConfig.debug.instantBelts = true; // globalConfig.debug.instantBelts = true;
// globalConfig.debug.instantProcessors = true; // globalConfig.debug.instantProcessors = true;
// globalConfig.debug.instantMiners = true; // globalConfig.debug.instantMiners = true;
globalConfig.debug.disableSavegameWrite = true; globalConfig.debug.disableSavegameWrite = true;
// globalConfig.beltSpeedItemsPerSecond *= 2; // globalConfig.beltSpeedItemsPerSecond *= 2;
} }
if (globalConfig.debug.fastGameEnter) { if (globalConfig.debug.fastGameEnter) {
globalConfig.debug.noArtificalDelays = true; globalConfig.debug.noArtificalDelays = true;
} }

View File

@ -1,114 +1,114 @@
export default { export default {
// You can set any debug options here! // You can set any debug options here!
/* dev:start */ /* dev:start */
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Quickly enters the game and skips the main menu - good for fast iterating _fastGameEnter: "Quickly enters the game and skips the main menu - good for fast iterating",
// fastGameEnter: true, fastGameEnter: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Skips any delays like transitions between states and such _noArtificialDelays: "Skips any delays like transitions between states and such",
// noArtificialDelays: true, noArtificialDelays: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Disables writing of savegames, useful for testing the same savegame over and over _disableSavegameWrite: "Disables writing of savegames, useful for testing the same savegame over and over",
// disableSavegameWrite: true, disableSavegameWrite: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Shows bounds of all entities _showEntityBounds: "Shows bounds of all entities",
// showEntityBounds: true, showEntityBounds: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Shows arrows for every ejector / acceptor _showAcceptorEjectors: "Shows arrows for every ejector / acceptor",
// showAcceptorEjectors: true, showAcceptorEjectors: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Disables the music (Overrides any setting, can cause weird behaviour) _disableMusic: "Disables the music (Overrides any setting, can cause weird behaviour)",
// disableMusic: true, disableMusic: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Do not render static map entities (=most buildings) _doNotRenderStatics: "Do not render static map entities (=most buildings)",
// doNotRenderStatics: true, doNotRenderStatics: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Allow to zoom freely without limits _disableZoomLimits: "Allow to zoom freely without limits",
// disableZoomLimits: true, disableZoomLimits: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Shows a border arround every chunk _showChunkBorders: "Shows a border arround every chunk",
// showChunkBorders: true, showChunkBorders: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// All rewards can be unlocked by passing just 1 of any shape _rewardsInstant: "All rewards can be unlocked by passing just 1 of any shape",
// rewardsInstant: true, rewardsInstant: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Unlocks all buildings _allBuildingsUnlocked: "Unlocks all buildings",
// allBuildingsUnlocked: true, allBuildingsUnlocked: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Disables cost of blueprints _blueprintsNoCost: "Disables cost of blueprints",
// blueprintsNoCost: true, blueprintsNoCost: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Disables cost of upgrades _upgradesNoCost: "Disables cost of upgrades",
// upgradesNoCost: true, upgradesNoCost: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Disables the dialog when completing a level _disableUnlockDialog: "Disables the dialog when completing a level",
// disableUnlockDialog: true, disableUnlockDialog: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Disables the simulation - This effectively pauses the game. _disableLogicTicks: "Disables the simulation - This effectively pauses the game.",
// disableLogicTicks: true, disableLogicTicks: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Test the rendering if everything is clipped out properly _testClipping: "Test the rendering if everything is clipped out properly",
// testClipping: true, testClipping: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Allows to render slower, useful for recording at half speed to avoid stuttering // Allows to render slower, useful for recording at half speed to avoid stuttering
// framePausesBetweenTicks: 250, // framePausesBetweenTicks: 250,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Replace all translations with emojis to see which texts are translateable _testTranslations: "Replace all translations with emojis to see which texts are translateable",
// testTranslations: true, testTranslations: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Enables an inspector which shows information about the entity below the curosr _enableEntityInspector: "Enables an inspector which shows information about the entity below the curosr",
// enableEntityInspector: true, enableEntityInspector: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Enables ads in the local build (normally they are deactivated there) _testAds: "Enables ads in the local build (normally they are deactivated there)",
// testAds: true, testAds: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Disables the automatic switch to an overview when zooming out _disableMapOverview: "Disables the automatic switch to an overview when zooming out",
// disableMapOverview: true, disableMapOverview: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Disables the notification when there are new entries in the changelog since last played _disableUpgradeNotification: "Disables the notification when there are new entries in the changelog since last played",
// disableUpgradeNotification: true, disableUpgradeNotification: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Makes belts almost infinitely fast _instantBelts: "Makes belts almost infinitely fast",
// instantBelts: true, instantBelts: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Makes item processors almost infinitely fast _instantProcessors: "Makes item processors almost infinitely fast",
// instantProcessors: true, instantProcessors: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Makes miners almost infinitely fast _instantMiners: "Makes miners almost infinitely fast",
// instantMiners: true, instantMiners: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// When using fastGameEnter, controls whether a new game is started or the last one is resumed _resumeGameOnFastEnter: "When using fastGameEnter, controls whether a new game is started or the last one is resumed",
// resumeGameOnFastEnter: true, resumeGameOnFastEnter: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Special option used to render the trailer _renderForTrailer: "Special option used to render the trailer",
// renderForTrailer: true, renderForTrailer: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Whether to render changes _renderChanges: "Whether to render changes",
// renderChanges: true, renderChanges: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Whether to render belt paths _renderBeltPaths: "Whether to render belt paths",
// renderBeltPaths: true, renderBeltPaths: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Whether to check belt paths _checkBeltPaths: "Whether to check belt paths",
// checkBeltPaths: true, checkBeltPaths: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Whether to items / s instead of items / m in stats _detailedStatistics: "Whether to items / s instead of items / m in stats",
// detailedStatistics: true, detailedStatistics: false,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Shows detailed information about which atlas is used // Shows detailed information about which atlas is used
// showAtlasInfo: true, // showAtlasInfo: true,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Renders the rotation of all wires // Renders the rotation of all wires
// renderWireRotations: true, // renderWireRotations: true,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Renders information about wire networks // Renders information about wire networks
// renderWireNetworkInfos: true, // renderWireNetworkInfos: true,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Disables ejector animations and processing // Disables ejector animations and processing
// disableEjectorProcessing: true, // disableEjectorProcessing: true,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Allows manual ticking // Allows manual ticking
// manualTickOnly: true, // manualTickOnly: true,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
/* dev:end */ /* dev:end */
}; };

View File

@ -71,7 +71,23 @@ export class TextualGameState extends GameState {
backToStatePayload: this.backToStatePayload, backToStatePayload: this.backToStatePayload,
}, },
}); });
} }
/**
* Goes to a new state, telling him to go back to this state later
* @param {string} stateId
*/
switchToState(stateId) {
// debugger;
this.moveToState(
stateId,
{
backToStateId: this.backToStateId,
backToStatePayload: this.backToStatePayload,
},
true
);
}
/** /**
* Removes all click detectors, except the one on the back button. Useful when regenerating * Removes all click detectors, except the one on the back button. Useful when regenerating

View File

@ -19,7 +19,7 @@ import { Vector } from "../core/vector";
/** /**
* Stores a lookup table for all building variants (for better performance) * Stores a lookup table for all building variants (for better performance)
* @type {Object<number, BuildingVariantIdentifier>} * @type {{[x: string]: BuildingVariantIdentifier, [x: number]: BuildingVariantIdentifier}}
*/ */
export const gBuildingVariants = { export const gBuildingVariants = {
// Set later // Set later
@ -27,7 +27,7 @@ export const gBuildingVariants = {
/** /**
* Registers a new variant * Registers a new variant
* @param {number} id * @param {number | string} id
* @param {typeof MetaBuilding} meta * @param {typeof MetaBuilding} meta
* @param {string} variant * @param {string} variant
* @param {number} rotationVariant * @param {number} rotationVariant
@ -50,7 +50,7 @@ export function registerBuildingVariant(
/** /**
* *
* @param {number} code * @param {number | string} code
* @returns {BuildingVariantIdentifier} * @returns {BuildingVariantIdentifier}
*/ */
export function getBuildingDataFromCode(code) { export function getBuildingDataFromCode(code) {
@ -72,12 +72,12 @@ export function getCodeFromBuildingData(metaBuilding, variant, rotationVariant)
data.variant === variant && data.variant === variant &&
data.rotationVariant === rotationVariant data.rotationVariant === rotationVariant
) { ) {
return +key; return key;
} }
} }
assertAlways( assertAlways(
false, false,
"Building not found by data: " + metaBuilding.getId() + " / " + variant + " / " + rotationVariant "Building not found by data: " + metaBuilding.getId() + " / " + variant + " / " + rotationVariant
); );
return 0; return "0";
} }

View File

@ -44,7 +44,7 @@ export function initComponentRegistry() {
assert( assert(
// @ts-ignore // @ts-ignore
require.context("./components", false, /.*\.js/i).keys().length === require.context("./components", false, /.*\.js/i).keys().length >=
gComponentRegistry.getNumEntries(), gComponentRegistry.getNumEntries(),
"Not all components are registered" "Not all components are registered"
); );

View File

@ -19,7 +19,7 @@ export class StaticMapEntityComponent extends Component {
originalRotation: types.float, originalRotation: types.float,
// See building_codes.js // See building_codes.js
code: types.uint, code: types.string,
}; };
} }
@ -79,7 +79,7 @@ export class StaticMapEntityComponent extends Component {
* @param {Vector=} param0.tileSize Size of the entity in tiles * @param {Vector=} param0.tileSize Size of the entity in tiles
* @param {number=} param0.rotation Rotation in degrees. Must be multiple of 90 * @param {number=} param0.rotation Rotation in degrees. Must be multiple of 90
* @param {number=} param0.originalRotation Original Rotation in degrees. Must be multiple of 90 * @param {number=} param0.originalRotation Original Rotation in degrees. Must be multiple of 90
* @param {number=} param0.code Building code * @param {(number | string)=} param0.code Building code
*/ */
constructor({ constructor({
origin = new Vector(), origin = new Vector(),
@ -96,7 +96,7 @@ export class StaticMapEntityComponent extends Component {
this.origin = origin; this.origin = origin;
this.rotation = rotation; this.rotation = rotation;
this.code = code; this.code = code.toString();
this.originalRotation = originalRotation; this.originalRotation = originalRotation;
} }

View File

@ -189,7 +189,7 @@ export class Entity extends BasicSerializableObject {
const acceptorComp = this.components.ItemAcceptor; const acceptorComp = this.components.ItemAcceptor;
if (acceptorComp) { if (acceptorComp) {
const acceptorSprite = Loader.getSprite("sprites/misc/acceptor_slot.png"); const acceptorSprite = Loader.getSprite("sprites/debug/acceptor_slot.png");
for (let i = 0; i < acceptorComp.slots.length; ++i) { for (let i = 0; i < acceptorComp.slots.length; ++i) {
const slot = acceptorComp.slots[i]; const slot = acceptorComp.slots[i];
const slotTile = staticComp.localTileToWorld(slot.pos); const slotTile = staticComp.localTileToWorld(slot.pos);

View File

@ -22,6 +22,7 @@ import { LeverSystem } from "./systems/lever";
import { DisplaySystem } from "./systems/display"; import { DisplaySystem } from "./systems/display";
import { ItemProcessorOverlaysSystem } from "./systems/item_processor_overlays"; import { ItemProcessorOverlaysSystem } from "./systems/item_processor_overlays";
import { BeltReaderSystem } from "./systems/belt_reader"; import { BeltReaderSystem } from "./systems/belt_reader";
import { ModSystems, logger as GeoZLogger } from "../GeoZ/main";
const logger = createLogger("game_system_manager"); const logger = createLogger("game_system_manager");
@ -153,6 +154,55 @@ export class GameSystemManager {
add("itemProcessorOverlays", ItemProcessorOverlaysSystem); add("itemProcessorOverlays", ItemProcessorOverlaysSystem);
for (const system of ModSystems) {
const before = system.getUpdateBefore();
const after = system.getUpdateAfter();
const system_id = system.getId();
let override = false;
if (this.systems[system_id]) {
GeoZLogger.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;
}
GeoZLogger.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;
}
GeoZLogger.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) {
GeoZLogger.log(
`System "${system_id}" update order : ${this.systemUpdateOrder.indexOf(system_id)}`
);
}
}
logger.log("📦 There are", this.systemUpdateOrder.length, "game systems"); logger.log("📦 There are", this.systemUpdateOrder.length, "game systems");
} }

View File

@ -2,11 +2,13 @@ import { globalConfig } from "../core/config";
import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils"; import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils";
import { BasicSerializableObject, types } from "../savegame/serialization"; import { BasicSerializableObject, types } from "../savegame/serialization";
import { enumColors } from "./colors"; import { enumColors } from "./colors";
import { allShapeData } from "./shapes";
import { enumItemProcessorTypes } from "./components/item_processor"; import { enumItemProcessorTypes } from "./components/item_processor";
import { GameRoot } from "./root"; import { GameRoot } from "./root";
import { enumSubShape, ShapeDefinition } from "./shape_definition"; import { ShapeDefinition, ShapeLayer } from "./shape_definition";
import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals"; import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals";
import { UPGRADES } from "./upgrades"; import { UPGRADES } from "./upgrades";
import { ModProcessors } from "../GeoZ/main";
export class HubGoals extends BasicSerializableObject { export class HubGoals extends BasicSerializableObject {
static getId() { static getId() {
@ -324,16 +326,16 @@ export class HubGoals extends BasicSerializableObject {
*/ */
createRandomShape() { createRandomShape() {
const layerCount = clamp(this.level / 25, 2, 4); const layerCount = clamp(this.level / 25, 2, 4);
/** @type {Array<import("./shape_definition").ShapeLayer>} */ /** @type {Array<ShapeLayer>} */
let layers = []; let layers = [];
const randomColor = () => randomChoice(Object.values(enumColors)); const randomColor = () => randomChoice(Object.values(enumColors));
const randomShape = () => randomChoice(Object.values(enumSubShape)); const randomShape = () => randomChoice(Object.values(allShapeData).map(d => d.id));
let anyIsMissingTwo = false; let anyIsMissingTwo = false;
for (let i = 0; i < layerCount; ++i) { for (let i = 0; i < layerCount; ++i) {
/** @type {import("./shape_definition").ShapeLayer} */ /** @type {ShapeLayer} */
const layer = [null, null, null, null]; const layer = [null, null, null, null];
for (let quad = 0; quad < 4; ++quad) { for (let quad = 0; quad < 4; ++quad) {
@ -439,8 +441,17 @@ export class HubGoals extends BasicSerializableObject {
globalConfig.buildingSpeeds[processorType] globalConfig.buildingSpeeds[processorType]
); );
} }
default: default: {
if (ModProcessors[processorType]) {
return (
globalConfig.beltSpeedItemsPerSecond *
this.upgradeImprovements.processors *
globalConfig.buildingSpeeds[processorType]
);
}
assertAlways(false, "invalid processor type: " + processorType); assertAlways(false, "invalid processor type: " + processorType);
}
} }
return 1 / globalConfig.beltSpeedItemsPerSecond; return 1 / globalConfig.beltSpeedItemsPerSecond;

View File

@ -13,8 +13,12 @@ import { MetaLeverBuilding } from "../../buildings/lever";
import { MetaFilterBuilding } from "../../buildings/filter"; import { MetaFilterBuilding } from "../../buildings/filter";
import { MetaDisplayBuilding } from "../../buildings/display"; import { MetaDisplayBuilding } from "../../buildings/display";
import { MetaReaderBuilding } from "../../buildings/reader"; import { MetaReaderBuilding } from "../../buildings/reader";
import { MetaBuilding } from "../../meta_building";
const supportedBuildings = [ /**
* @type {Array<typeof MetaBuilding>}
*/
export const supportedBuildings = [
MetaBeltBaseBuilding, MetaBeltBaseBuilding,
MetaSplitterBuilding, MetaSplitterBuilding,
MetaUndergroundBeltBuilding, MetaUndergroundBeltBuilding,

View File

@ -7,7 +7,7 @@ import { Application } from "../application";
import { Signal, STOP_PROPAGATION } from "../core/signal"; import { Signal, STOP_PROPAGATION } from "../core/signal";
import { IS_MOBILE } from "../core/config"; import { IS_MOBILE } from "../core/config";
import { T } from "../translations"; import { T } from "../translations";
function key(str) { export function key(str) {
return str.toUpperCase().charCodeAt(0); return str.toUpperCase().charCodeAt(0);
} }

View File

@ -5,10 +5,11 @@ import { clamp, fastArrayDeleteValueIfContained, make2DUndefinedArray } from "..
import { Vector } from "../core/vector"; import { Vector } from "../core/vector";
import { BaseItem } from "./base_item"; import { BaseItem } from "./base_item";
import { enumColors } from "./colors"; import { enumColors } from "./colors";
import { allShapeData } from "./shapes";
import { Entity } from "./entity"; import { Entity } from "./entity";
import { COLOR_ITEM_SINGLETONS } from "./items/color_item"; import { COLOR_ITEM_SINGLETONS } from "./items/color_item";
import { GameRoot } from "./root"; import { GameRoot } from "./root";
import { enumSubShape } from "./shape_definition"; import { enumSubShape } from "./shapes";
import { Rectangle } from "../core/rectangle"; import { Rectangle } from "../core/rectangle";
const logger = createLogger("map_chunk"); const logger = createLogger("map_chunk");
@ -180,56 +181,58 @@ export class MapChunk {
*/ */
internalGenerateShapePatch(rng, shapePatchSize, distanceToOriginInChunks) { internalGenerateShapePatch(rng, shapePatchSize, distanceToOriginInChunks) {
/** @type {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} */ /** @type {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} */
let subShapes = null; let quads = null;
let weights = {}; let weights = {};
for (let s in allShapeData) {
// Later there is a mix of everything const data = allShapeData[s];
weights = { if (!data.spawnData || distanceToOriginInChunks < data.spawnData.minDistance) {
[enumSubShape.rect]: 100, continue;
[enumSubShape.circle]: Math.round(50 + clamp(distanceToOriginInChunks * 2, 0, 50)), }
[enumSubShape.star]: Math.round(20 + clamp(distanceToOriginInChunks, 0, 30)), const chances = data.spawnData.chances;
[enumSubShape.windmill]: Math.round(6 + clamp(distanceToOriginInChunks / 2, 0, 20)), const chance = Math.round(
}; clamp(
chances.min + (distanceToOriginInChunks - data.spawnData.minDistance) * chances.distanceMultiplier,
if (distanceToOriginInChunks < 7) { 0,
// Initial chunks can not spawn the good stuff chances.max
weights[enumSubShape.star] = 0; )
weights[enumSubShape.windmill] = 0; );
} if (chance) {
weights[data.id] = chance;
if (distanceToOriginInChunks < 10) {
// Initial chunk patches always have the same shape
const subShape = this.internalGenerateRandomSubShape(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);
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),
];
}
// Makes sure windmills never spawn as whole
let windmillCount = 0;
for (let i = 0; i < subShapes.length; ++i) {
if (subShapes[i] === enumSubShape.windmill) {
++windmillCount;
} }
} }
if (windmillCount > 1) { quads = [
subShapes[0] = enumSubShape.rect; this.internalGenerateRandomSubShape(rng, weights),
subShapes[1] = enumSubShape.rect; this.internalGenerateRandomSubShape(rng, weights),
this.internalGenerateRandomSubShape(rng, weights),
this.internalGenerateRandomSubShape(rng, weights),
];
if (distanceToOriginInChunks < 10) {
// Initial chunk patches always have the same shape
quads = [quads[0], quads[0], quads[0], quads[0]];
} else if (distanceToOriginInChunks < 15) {
// Later patches can also have mixed ones
quads = [quads[0], quads[0], quads[1], quads[1]];
} else {
// if (quads[0] == quads[2] && quads[0] != quads[3] && quads[0] != quads[1]) {
// quads = [quads[0], quads[2], quads[1], quads[3]];
// }
// if (quads[1] == quads[3] && quads[1] != quads[0] && quads[1] != quads[2]) {
// quads = [quads[0], quads[2], quads[1], quads[3]];
// }
} }
const definition = this.root.shapeDefinitionMgr.getDefinitionFromSimpleShapes(subShapes); if (
quads.filter(q => q == quads[0]).length > allShapeData[quads[0]].spawnData.maxQuarters ||
quads.filter(q => q == quads[1]).length > allShapeData[quads[1]].spawnData.maxQuarters ||
quads.filter(q => q == quads[2]).length > allShapeData[quads[2]].spawnData.maxQuarters
) {
return this.internalGenerateShapePatch(rng, shapePatchSize, distanceToOriginInChunks);
}
let colors = /** @type {[string, string, string, string]} */ (quads.map(q => allShapeData[q].spawnData.color));
const definition = this.root.shapeDefinitionMgr.getDefinitionFromSimpleShapesAndColors(quads, colors);
this.internalGeneratePatch( this.internalGeneratePatch(
rng, rng,
shapePatchSize, shapePatchSize,

View File

@ -6,6 +6,7 @@ import { Vector } from "../core/vector";
import { BasicSerializableObject, types } from "../savegame/serialization"; import { BasicSerializableObject, types } from "../savegame/serialization";
import { enumColors, enumColorsToHexCode, enumColorToShortcode, enumShortcodeToColor } from "./colors"; import { enumColors, enumColorsToHexCode, enumColorToShortcode, enumShortcodeToColor } from "./colors";
import { THEME } from "./theme"; import { THEME } from "./theme";
import { allShapeData, ShapeData, enumShortcodeToSubShape, enumSubShapeToShortcode, enumSubShape } from "./shapes";
/** /**
* @typedef {{ * @typedef {{
@ -26,28 +27,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 * Converts the given parameters to a valid shape definition
* @param {*} layers * @param {*} layers
@ -85,6 +64,7 @@ export class ShapeDefinition extends BasicSerializableObject {
return errorCode; return errorCode;
} }
const definition = ShapeDefinition.fromShortKey(data); const definition = ShapeDefinition.fromShortKey(data);
/** @type {Array<ShapeLayer>} */
this.layers = /** @type {Array<ShapeLayer>} */ (definition.layers); this.layers = /** @type {Array<ShapeLayer>} */ (definition.layers);
} }
@ -336,97 +316,98 @@ export class ShapeDefinition extends BasicSerializableObject {
for (let layerIndex = 0; layerIndex < this.layers.length; ++layerIndex) { for (let layerIndex = 0; layerIndex < this.layers.length; ++layerIndex) {
const quadrants = this.layers[layerIndex]; const quadrants = this.layers[layerIndex];
let quads = quadrants
.map((e, i) => ({ e, i }))
.filter(e => e.e)
.map(e => ({ ...e.e, quadrantIndex: e.i }))
const layerScale = Math.max(0.1, 0.9 - layerIndex * 0.22); const layerScale = Math.max(0.1, 0.9 - layerIndex * 0.22);
for (let quadrantIndex = 0; quadrantIndex < 4; ++quadrantIndex) { for (let quad of quads) {
if (!quadrants[quadrantIndex]) { if (!quad) {
continue;
}
const { subShape, color, quadrantIndex } = quad;
if (subShape == "-") {
continue; continue;
} }
const { subShape, color } = quadrants[quadrantIndex];
const quadrantPos = arrayQuadrantIndexToOffset[quadrantIndex]; const quadrantPos = arrayQuadrantIndexToOffset[quadrantIndex];
const centerQuadrantX = quadrantPos.x * quadrantHalfSize; const centerQuadrantX = quadrantPos.x * quadrantHalfSize;
const centerQuadrantY = quadrantPos.y * quadrantHalfSize; const centerQuadrantY = quadrantPos.y * quadrantHalfSize;
const rotation = Math.radians(quadrantIndex * 90); const rotation = Math.radians(quadrantIndex * 90);
context.save();
context.translate(centerQuadrantX, centerQuadrantY); context.translate(centerQuadrantX, centerQuadrantY);
context.rotate(rotation); context.rotate(rotation);
context.fillStyle = enumColorsToHexCode[color]; context.fillStyle = enumColorsToHexCode[color];
context.strokeStyle = THEME.items.outline; context.strokeStyle = THEME.items.outline;
context.lineWidth = THEME.items.outlineWidth; const lineWidth = THEME.items.outlineWidth * Math.pow(0.8, layerIndex);
context.lineWidth = lineWidth;
const insetPadding = 0.0; const insetPadding = 0.0;
switch (subShape) { const dims = quadrantSize * layerScale;
case enumSubShape.rect: { const innerDims = insetPadding - quadrantHalfSize;
context.beginPath();
const dims = quadrantSize * layerScale; let began = null;
context.rect( // eslint-disable-next-line no-inner-declarations
insetPadding + -quadrantHalfSize, /** @type {import("./shapes").BeginDrawShape} */
-insetPadding + quadrantHalfSize - dims, function begin(args) {
dims, context.save();
dims context.translate(innerDims, -innerDims);
); context.scale(dims, -dims);
context.lineWidth = lineWidth / dims / (args.scale || 1);
break; if (args.scale) {
context.scale(args.scale, args.scale);
} }
case enumSubShape.star: { if (args.beginPath) {
context.beginPath(); context.beginPath();
const dims = quadrantSize * layerScale; }
if (args.moveToZero) {
let originX = insetPadding - quadrantHalfSize; context.moveTo(0, 0);
let originY = -insetPadding + quadrantHalfSize - dims; }
began = args;
const moveInwards = dims * 0.4; }
context.moveTo(originX, originY + moveInwards); // eslint-disable-next-line no-inner-declarations
context.lineTo(originX + dims, originY); function end() {
context.lineTo(originX + dims - moveInwards, originY + dims); if (!began) {
context.lineTo(originX, originY + dims); return;
}
if (began.path) {
context.closePath(); context.closePath();
break;
}
case enumSubShape.windmill: {
context.beginPath();
const dims = quadrantSize * layerScale;
let originX = insetPadding - quadrantHalfSize;
let originY = -insetPadding + quadrantHalfSize - dims;
const moveInwards = dims * 0.4;
context.moveTo(originX, originY + moveInwards);
context.lineTo(originX + dims, originY);
context.lineTo(originX + dims, originY + dims);
context.lineTo(originX, originY + dims);
context.closePath();
break;
}
case enumSubShape.circle: {
context.beginPath();
context.moveTo(insetPadding + -quadrantHalfSize, -insetPadding + quadrantHalfSize);
context.arc(
insetPadding + -quadrantHalfSize,
-insetPadding + quadrantHalfSize,
quadrantSize * layerScale,
-Math.PI * 0.5,
0
);
context.closePath();
break;
}
default: {
assertAlways(false, "Unkown sub shape: " + subShape);
} }
context.restore();
} }
context.fill(); /** @type {ShapeData} */
context.stroke(); let shape = allShapeData[subShape];
assertAlways(shape.draw, "shape should be drawable!");
if (typeof shape.draw === "string") {
let draw = shape.draw;
begin({ scale: 1 });
let p = new Path2D(draw);
context.fill(p);
context.stroke(p);
end();
} else {
shape.draw({
dims,
innerDims,
layer: layerIndex,
quadrant: quadrantIndex,
context,
color,
begin,
});
end();
context.fill();
context.stroke();
}
context.rotate(-rotation); context.restore();
context.translate(-centerQuadrantX, -centerQuadrantY);
} }
} }
} }

View File

@ -3,7 +3,8 @@ import { BasicSerializableObject } from "../savegame/serialization";
import { enumColors } from "./colors"; import { enumColors } from "./colors";
import { ShapeItem } from "./items/shape_item"; import { ShapeItem } from "./items/shape_item";
import { GameRoot } from "./root"; import { GameRoot } from "./root";
import { enumSubShape, ShapeDefinition } from "./shape_definition"; import { ShapeDefinition } from "./shape_definition";
import { enumSubShape } from "./shapes";
const logger = createLogger("shape_definition_manager"); const logger = createLogger("shape_definition_manager");
@ -256,4 +257,18 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
return this.registerOrReturnHandle(new ShapeDefinition({ layers: [shapeLayer] })); return this.registerOrReturnHandle(new ShapeDefinition({ layers: [shapeLayer] }));
} }
/**
*
* @param {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} subShapes
* @param {[string, string, string, string]} colors
* @returns {ShapeDefinition}
*/
getDefinitionFromSimpleShapesAndColors(subShapes, colors) {
const shapeLayer = /** @type {import("./shape_definition").ShapeLayer} */ (subShapes.map(
(subShape, i) => ({ subShape, color: colors[i] })
));
return this.registerOrReturnHandle(new ShapeDefinition({ layers: [shapeLayer] }));
}
} }

181
src/js/game/shapes.js Normal file
View File

@ -0,0 +1,181 @@
/** @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;
}
/**
* @callback BeginDrawShape
* @param {{
* scale?: number,
* beginPath?: boolean,
* moveToZero?: true
* }} args
*/
/**
* @typedef {Object} DrawShapeParams
* @property {number} dims
* @property {number} innerDims
* @property {number} layer
* @property {number} quadrant
* @property {CanvasRenderingContext2D} context
* @property {string} color
* @property {BeginDrawShape} begin
*/
/**
* @callback DrawShape
* @param {DrawShapeParams} args
*/
/**
* @typedef {Object} SpawnChanceData
* @property {number} [min=0]
* @property {number} [max=100]
* @property {number} [distanceMultiplier=1]
*/
/**
* @typedef {Object} ShapeSpawnData
* @property {string} [color="uncolored"]
* @property {number} [minDistance=0]
* @property {number} [maxQuarters=4]
* @property {SpawnChanceData} [chances]
*/
/**
* @typedef {Object} ShapeData
* @property {string} id
* @property {string} code
* @property {DrawShape | string} draw
* @property {number} tier
* @property {ShapeSpawnData} [spawnData]
*/
/** @type {Object<string, ShapeData>} */
export const allShapeData = {
rect: {
id: "rect",
code: "R",
draw: "M 0 0 v 1 h 1 v -1 z",
tier: 0,
spawnData: {
color: "uncolored",
maxQuarters: 4,
minDistance: 0,
chances: {
min: 100,
distanceMultiplier: 0,
max: 100,
},
},
},
circle: {
id: "circle",
code: "C",
draw: "M 0 0 l 1 0 a 1 1 0 0 1 -1 1 z ",
tier: 0,
spawnData: {
color: "uncolored",
maxQuarters: 4,
minDistance: 0,
chances: {
min: 50,
distanceMultiplier: 15,
max: 100,
},
},
},
star: {
id: "star",
code: "S",
draw: "M 0 0 L 0 0.6 1 1 0.6 0 z",
tier: 0.5,
spawnData: {
color: "uncolored",
maxQuarters: 4,
minDistance: 5,
chances: {
min: 20,
distanceMultiplier: 10,
max: 100,
},
},
},
windmill: {
id: "windmill",
code: "W",
draw: "M 0 0 L 0 0.6 1 1 1 0 z",
tier: 1,
spawnData: {
color: "uncolored",
maxQuarters: 3,
minDistance: 7,
chances: {
min: 20,
distanceMultiplier: 5,
max: 100,
},
},
},
};
export function initShapes() {
for (let k in enumSubShape) {
delete enumSubShape[k];
}
for (let k in enumSubShapeToShortcode) {
delete enumSubShapeToShortcode[k];
}
for (let k in enumShortcodeToSubShape) {
delete enumShortcodeToSubShape[k];
}
for (let s in allShapeData) {
let data = allShapeData[s];
assert(data.id == s);
assert(data.code.toUpperCase() == data.code);
assert(data.draw);
enumSubShape[data.id] = data.id;
enumSubShapeToShortcode[data.id] = data.code;
enumShortcodeToSubShape[data.code] = data.id;
if (data.spawnData) {
const sdata = data.spawnData;
sdata.color = sdata.color || "uncolored";
sdata.maxQuarters = sdata.maxQuarters || 4;
sdata.minDistance = sdata.minDistance || 0;
if(sdata.chances) {
const chances = sdata.chances;
chances.min = chances.min || 0;
chances.max = chances.max || 100;
chances.distanceMultiplier = chances.distanceMultiplier || 1;
} else {
sdata.chances = {
min: 0,
max: 100,
distanceMultiplier: 1
};
}
}
}
}

View File

@ -238,8 +238,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const items = processorComp.inputSlots; const items = processorComp.inputSlots;
processorComp.inputSlots = []; processorComp.inputSlots = [];
/** @type {Object.<string, { item: BaseItem, sourceSlot: number }>} */ /** @type {Array<{ item: BaseItem, sourceSlot: number }>} */
const itemsBySlot = {}; const itemsBySlot = [];
for (let i = 0; i < items.length; ++i) { for (let i = 0; i < items.length; ++i) {
itemsBySlot[items[i].sourceSlot] = items[i]; itemsBySlot[items[i].sourceSlot] = items[i];
} }
@ -251,292 +251,309 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
let trackProduction = true; let trackProduction = true;
// DO SOME MAGIC // 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) { let nextSlot = processorComp.nextOutputSlot++ % availableSlots;
// SPLITTER for (let i = 0; i < items.length; ++i) {
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()) {
outItems.push({ outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition), item: items[i].item,
requiredSlot: i, 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) const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutHalf(inputDefinition);
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.shapeActionCutQuad(inputDefinition); for (let i = 0; i < cutDefinitions.length; ++i) {
const definition = cutDefinitions[i];
for (let i = 0; i < cutDefinitions.length; ++i) { if (!definition.isEntirelyEmpty()) {
const definition = cutDefinitions[i]; outItems.push({
if (!definition.isEntirelyEmpty()) { item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
outItems.push({ requiredSlot: i,
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 const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutQuad(inputDefinition);
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 rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(inputDefinition); for (let i = 0; i < cutDefinitions.length; ++i) {
outItems.push({ const definition = cutDefinitions[i];
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition), if (!definition.isEntirelyEmpty()) {
}); outItems.push({
break; item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
} requiredSlot: i,
});
// 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<enumColors>} */
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;
} }
break;
} }
const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith4Colors( // ROTATER
shapeItem.definition, case enumItemProcessorTypes.rotater: {
/** @type {[string, string, string, string]} */ (colors) const inputItem = /** @type {ShapeItem} */ (items[0].item);
); assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
const inputDefinition = inputItem.definition;
outItems.push({ const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition), inputDefinition
}); );
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({ outItems.push({
item, item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
requiredSlot: 1,
}); });
break; break;
} }
const value = network.currentValue; // ROTATER (CCW)
if (value.equals(BOOL_TRUE_SINGLETON) || value.equals(item)) { 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({ outItems.push({
item, item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
requiredSlot: 0,
});
} else {
outItems.push({
item,
requiredSlot: 1,
}); });
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 const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateFL(
case enumItemProcessorTypes.reader: { inputDefinition
// Pass through the item );
const item = itemsBySlot[0].item; outItems.push({
outItems.push({ item }); item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
});
// Track the item break;
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; // STACKER
}
default: case enumItemProcessorTypes.stacker: {
assertAlways(false, "Unkown item processor type: " + processorComp.type); 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<enumColors>} */
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 // Track produced items

View File

@ -25,6 +25,12 @@ export class LogicGateSystem extends GameSystemWithFilter {
[enumLogicGateType.unstacker]: this.compute_UNSTACK.bind(this), [enumLogicGateType.unstacker]: this.compute_UNSTACK.bind(this),
[enumLogicGateType.shapecompare]: this.compute_SHAPECOMPARE.bind(this), [enumLogicGateType.shapecompare]: this.compute_SHAPECOMPARE.bind(this),
}; };
const { ModWireProcessors } = require("../../GeoZ/main");
for (const type in ModWireProcessors) {
//@ts-ignore
this.boundOperations[type] = ModWireProcessors[type].compute.bind(null, this);
}
} }
update() { update() {

View File

@ -10,6 +10,7 @@ import { initDrawUtils } from "./core/draw_utils";
import { initItemRegistry } from "./game/item_registry"; import { initItemRegistry } from "./game/item_registry";
import { initMetaBuildingRegistry } from "./game/meta_building_registry"; import { initMetaBuildingRegistry } from "./game/meta_building_registry";
import { initGameSpeedRegistry } from "./game/game_speed_registry"; import { initGameSpeedRegistry } from "./game/game_speed_registry";
import { initMods } from "./GeoZ/main";
const logger = createLogger("main"); const logger = createLogger("main");
@ -18,37 +19,6 @@ if (window.coreThreadLoadedCb) {
window.coreThreadLoadedCb(); window.coreThreadLoadedCb();
} }
// Logrocket
// if (!G_IS_DEV && !G_IS_STANDALONE) {
// const monthlyUsers = 300; // thousand
// const logrocketLimit = 10; // thousand
// const percentageOfUsers = logrocketLimit / monthlyUsers;
// if (Math.random() <= percentageOfUsers) {
// logger.log("Analyzing this session with logrocket");
// const logrocket = require("logrocket");
// logrocket.init("p1x9zh/shapezio");
// try {
// logrocket.getSessionURL(function (sessionURL) {
// logger.log("Connected lockrocket to GA");
// // @ts-ignore
// try {
// window.ga("send", {
// hitType: "event",
// eventCategory: "LogRocket",
// eventAction: sessionURL,
// });
// } catch (ex) {
// logger.warn("Logrocket connection to analytics failed:", ex);
// }
// });
// } catch (ex) {
// logger.warn("Logrocket connection to analytics failed:", ex);
// }
// }
// }
console.log( console.log(
`%cshapez.io %c\n© 2020 Tobias Springer IT Solutions\nCommit %c${G_BUILD_COMMIT_HASH}%c on %c${new Date( `%cshapez.io %c\n© 2020 Tobias Springer IT Solutions\nCommit %c${G_BUILD_COMMIT_HASH}%c on %c${new Date(
G_BUILD_TIME G_BUILD_TIME
@ -77,12 +47,6 @@ console.log("%cDEVCODE BUILT IN", "color: #f77");
logSection("Boot Process", "#f9a825"); logSection("Boot Process", "#f9a825");
initDrawUtils();
initComponentRegistry();
initItemRegistry();
initMetaBuildingRegistry();
initGameSpeedRegistry();
let app = null; let app = null;
function bootApp() { function bootApp() {
@ -91,4 +55,4 @@ function bootApp() {
app.boot(); app.boot();
} }
window.addEventListener("load", bootApp); window.addEventListener("load", bootApp);

View File

@ -10,6 +10,7 @@ import { THEMES, THEME, applyGameTheme } from "../game/theme";
import { IS_DEMO } from "../core/config"; import { IS_DEMO } from "../core/config";
import { T } from "../translations"; import { T } from "../translations";
import { LANGUAGES } from "../languages"; import { LANGUAGES } from "../languages";
import { globalConfig, IS_DEBUG } from "../core/config";
const logger = createLogger("application_settings"); const logger = createLogger("application_settings");
@ -21,6 +22,8 @@ export const enumCategories = {
userInterface: "userInterface", userInterface: "userInterface",
performance: "performance", performance: "performance",
advanced: "advanced", advanced: "advanced",
debug: "debug",
keybindings: "keybindings",
}; };
export const uiScales = [ export const uiScales = [
@ -278,6 +281,21 @@ export const allApplicationSettings = [
new BoolSetting("lowQualityTextures", enumCategories.performance, (app, value) => {}), new BoolSetting("lowQualityTextures", enumCategories.performance, (app, value) => {}),
]; ];
if (IS_DEBUG) {
for (let k in globalConfig.debug) {
if (k.startsWith("_")) continue;
const setting = new BoolSetting(`debug_${k}`, enumCategories.debug, (app, value) => {
globalConfig.debug[k] = value;
});
setting.validate = () => true;
T.settings.labels[`debug_${k}`] = {
title: k.replace(/(?!^)([A-Z])/g, " $1"),
description: globalConfig.debug[`_${k}`],
};
allApplicationSettings.push(setting);
}
}
export function getApplicationSettingById(id) { export function getApplicationSettingById(id) {
return allApplicationSettings.find(setting => setting.id === id); return allApplicationSettings.find(setting => setting.id === id);
} }
@ -358,7 +376,9 @@ export class ApplicationSettings extends ReadWriteProxy {
* @param {string} key * @param {string} key
*/ */
getSetting(key) { getSetting(key) {
assert(this.getAllSettings().hasOwnProperty(key), "Setting not known: " + key); if (!key.startsWith("debug_")) {
assert(this.getAllSettings().hasOwnProperty(key), "Setting not known: " + key);
}
return this.getAllSettings()[key]; return this.getAllSettings()[key];
} }

View File

@ -185,6 +185,10 @@ export class BoolSetting extends BaseSetting {
} }
getHtml() { getHtml() {
if (!T.settings.labels[this.id].description) {
let a = T;
let b = a;
}
return ` return `
<div class="setting cardbox ${this.enabled ? "enabled" : "disabled"}"> <div class="setting cardbox ${this.enabled ? "enabled" : "disabled"}">
${this.enabled ? "" : `<span class="standaloneOnlyHint">${T.demo.settingNotAvailable}</span>`} ${this.enabled ? "" : `<span class="standaloneOnlyHint">${T.demo.settingNotAvailable}</span>`}

View File

@ -4,39 +4,58 @@ import { T } from "../translations";
import { KEYMAPPINGS, getStringForKeyCode } from "../game/key_action_mapper"; import { KEYMAPPINGS, getStringForKeyCode } from "../game/key_action_mapper";
import { Dialog } from "../core/modal_dialog_elements"; import { Dialog } from "../core/modal_dialog_elements";
import { IS_DEMO } from "../core/config"; import { IS_DEMO } from "../core/config";
import { SettingsState } from "./settings";
export class KeybindingsState extends TextualGameState { export class KeybindingsState extends SettingsState {
constructor() { // constructor() {
super("KeybindingsState"); // super();
} // super("KeybindingsState");
// this.settingsState = settingsState;
// }
getStateHeaderTitle() { // getStateHeaderTitle() {
return T.keybindings.title; // return T.keybindings.title;
} // }
getMainContentHTML() { getMainContentHTML() {
return ` return `
<div class="sidebar">
${this.getCategoryButtonsHtml()}
<div class="topEntries"> <div class="other">
<span class="hint">${T.keybindings.hint}</span> <button class="styledButton about">${T.about.title}</button>
<button class="styledButton resetBindings">${T.keybindings.resetKeybindings}</button>
<div class="versionbar">
<div class="buildVersion">${T.global.loading} ...</div>
</div>
</div>
</div> </div>
<div class="keybindings"> <div class="categoryContainer">
<div class="category keybindings" data-category="keybindings">
<div class="topEntries">
<span class="hint">${T.keybindings.hint}</span>
<button class="styledButton resetBindings">${T.keybindings.resetKeybindings}</button>
</div>
</div>
${this.getSettingsHtml()}
</div> </div>
`; `;
} }
onEnter() { onEnter() {
this.onEnterCommon();
const keybindingsElem = this.htmlElement.querySelector(".keybindings"); const keybindingsElem = this.htmlElement.querySelector(".keybindings");
this.trackClicks(this.htmlElement.querySelector(".resetBindings"), this.resetBindings); this.trackClicks(this.htmlElement.querySelector(".resetBindings"), this.resetBindings);
for (const category in KEYMAPPINGS) { for (const category in KEYMAPPINGS) {
const categoryDiv = document.createElement("div"); const categoryDiv = document.createElement("div");
categoryDiv.classList.add("category"); categoryDiv.classList.add("keyCategory");
keybindingsElem.appendChild(categoryDiv); keybindingsElem.appendChild(categoryDiv);
const labelDiv = document.createElement("strong"); const labelDiv = document.createElement("strong");
@ -173,7 +192,7 @@ export class KeybindingsState extends TextualGameState {
}); });
} }
getDefaultPreviousState() { // getDefaultPreviousState() {
return "SettingsState"; // return "SettingsState";
} // }
} }

View File

@ -75,7 +75,7 @@ export class MainMenuState extends GameState {
<div class="logo"> <div class="logo">
<img src="${cachebust("res/logo.png")}" alt="shapez.io Logo"> <img src="${cachebust("res/logo.png")}" alt="shapez.io Logo">
<span class="updateLabel">Wires update!</span> <span class="updateLabel">Geo<span style="color:#66bb6a">Z</span></span>
</div> </div>
@ -119,7 +119,7 @@ export class MainMenuState extends GameState {
<div class="author">${T.mainMenu.madeBy.replace( <div class="author">${T.mainMenu.madeBy.replace(
"<author-link>", "<author-link>",
'<a class="producerLink" target="_blank">Tobias Springer</a>' '<a class="producerLink" target="_blank">Tobias Springer & modded by Exund</a>'
)}</div> )}</div>
</div> </div>

View File

@ -8,6 +8,12 @@ import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
import { CHANGELOG } from "../changelog"; import { CHANGELOG } from "../changelog";
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { initComponentRegistry } from "../game/component_registry";
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";
const logger = createLogger("state/preload"); const logger = createLogger("state/preload");
export class PreloadState extends GameState { export class PreloadState extends GameState {
@ -108,6 +114,18 @@ export class PreloadState extends GameState {
return this.app.settings.initialize(); return this.app.settings.initialize();
}) })
.then(() => this.setStatus("Loading GeoZ mods"))
.then(() => require("../GeoZ/main").initMods())
.then(() => this.setStatus("Initializing registeries"))
.then(() => {
initDrawUtils();
initComponentRegistry();
initItemRegistry();
initMetaBuildingRegistry();
initGameSpeedRegistry();
})
.then(() => { .then(() => {
// Initialize fullscreen // Initialize fullscreen
if (this.app.platformWrapper.getSupportsFullscreen()) { if (this.app.platformWrapper.getSupportsFullscreen()) {

View File

@ -4,8 +4,8 @@ import { allApplicationSettings, enumCategories } from "../profile/application_s
import { T } from "../translations"; import { T } from "../translations";
export class SettingsState extends TextualGameState { export class SettingsState extends TextualGameState {
constructor() { constructor(key = "SettingsState") {
super("SettingsState"); super(key);
} }
getStateHeaderTitle() { getStateHeaderTitle() {
@ -92,7 +92,7 @@ export class SettingsState extends TextualGameState {
</span>`; </span>`;
} }
onEnter(payload) { onEnterCommon() {
this.renderBuildText(); this.renderBuildText();
this.trackClicks(this.htmlElement.querySelector(".about"), this.onAboutClicked, { this.trackClicks(this.htmlElement.querySelector(".about"), this.onAboutClicked, {
preventDefault: false, preventDefault: false,
@ -109,6 +109,12 @@ export class SettingsState extends TextualGameState {
this.htmlElement.querySelector(".category").classList.add("active"); this.htmlElement.querySelector(".category").classList.add("active");
this.htmlElement.querySelector(".categoryButton").classList.add("active"); this.htmlElement.querySelector(".categoryButton").classList.add("active");
this.setActiveCategory(enumCategories.general);
}
onEnter(payload) {
this.onEnterCommon();
} }
setActiveCategory(category) { setActiveCategory(category) {
@ -164,6 +170,6 @@ export class SettingsState extends TextualGameState {
} }
onKeybindingsClicked() { onKeybindingsClicked() {
this.moveToStateAddGoBack("KeybindingsState"); this.switchToState("KeybindingsState");
} }
} }

View File

@ -727,6 +727,8 @@ settings:
userInterface: User Interface userInterface: User Interface
advanced: Advanced advanced: Advanced
performance: Performance performance: Performance
debug: Debug
keybindings: Keybindings
versionBadges: versionBadges:
dev: Development dev: Development