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:
commit
94f32ad226
@ -163,7 +163,7 @@ function serve({ standalone }) {
|
||||
});
|
||||
|
||||
// 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
|
||||
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"));
|
||||
|
||||
// Watch translations
|
||||
gulp.watch("../translations/**/*.yaml", gulp.series("translations.convertToJson"));
|
||||
gulp.watch("../translations/**/*.yaml", { usePolling: true }, gulp.series("translations.convertToJson"));
|
||||
|
||||
gulp.watch(
|
||||
["../res_raw/sounds/sfx/*.mp3", "../res_raw/sounds/sfx/*.wav"],
|
||||
|
@ -14,6 +14,9 @@ module.exports = ({ watch = false, standalone = false }) => {
|
||||
"bundle.js": [path.resolve(__dirname, "../src/js/main.js")],
|
||||
},
|
||||
watch,
|
||||
watchOptions: {
|
||||
poll: 1000
|
||||
},
|
||||
node: {
|
||||
fs: "empty",
|
||||
},
|
||||
|
@ -1,5 +1,12 @@
|
||||
#state_KeybindingsState {
|
||||
#state_SettingsState {
|
||||
|
||||
$colorCategoryButton: #eee;
|
||||
$colorCategoryButtonSelected: #5f748b;
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
overflow-y: scroll;
|
||||
|
||||
.topEntries {
|
||||
display: grid;
|
||||
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 {
|
||||
|
@ -164,24 +164,28 @@
|
||||
|
||||
position: relative;
|
||||
.updateLabel {
|
||||
background-color: $mainBgColor;
|
||||
border-radius: 999em;
|
||||
padding: 0px 10px;
|
||||
position: absolute;
|
||||
transform: translateX(50%) rotate(-5deg);
|
||||
color: $colorRedBright;
|
||||
transform: translateX(50%) rotate(-5deg) scale(1.5);
|
||||
color: #7d808a;
|
||||
//color: $colorRedBright;
|
||||
@include Heading;
|
||||
text-transform: uppercase;
|
||||
//text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
@include S(right, 40px);
|
||||
@include S(bottom, 20px);
|
||||
|
||||
@include InlineAnimation(1.3s ease-in-out infinite) {
|
||||
50% {
|
||||
transform: translateX(50%) rotate(-7deg) scale(1.1);
|
||||
transform: translateX(50%) rotate(-7deg) scale(1.65);
|
||||
}
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
/*@include DarkThemeOverride {
|
||||
color: $colorBlueBright;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
|
221
src/js/GeoZ/main.js
Normal file
221
src/js/GeoZ/main.js
Normal 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
157
src/js/GeoZ/mod_building.js
Normal 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;
|
||||
}
|
||||
}
|
13
src/js/GeoZ/mod_component.js
Normal file
13
src/js/GeoZ/mod_component.js
Normal 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
5
src/js/GeoZ/mod_item.js
Normal file
@ -0,0 +1,5 @@
|
||||
import { BaseItem } from "../game/base_item";
|
||||
|
||||
export class ModItem extends BaseItem {
|
||||
|
||||
}
|
50
src/js/GeoZ/mod_processor.js
Normal file
50
src/js/GeoZ/mod_processor.js
Normal 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
121
src/js/GeoZ/mod_system.js
Normal 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
30
src/js/GeoZ/mod_utils.js
Normal 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);
|
||||
}
|
24
src/js/GeoZ/mod_wireprocessor.js
Normal file
24
src/js/GeoZ/mod_wireprocessor.js
Normal 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 [];
|
||||
}
|
||||
}
|
362
src/js/GeoZ/mods/test/test.mod.js
Normal file
362
src/js/GeoZ/mods/test/test.mod.js
Normal 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;
|
@ -145,7 +145,7 @@ export class Application {
|
||||
MobileWarningState,
|
||||
MainMenuState,
|
||||
InGameState,
|
||||
SettingsState,
|
||||
//SettingsState,
|
||||
KeybindingsState,
|
||||
AboutState,
|
||||
ChangelogState,
|
||||
|
@ -93,7 +93,7 @@ export const globalConfig = {
|
||||
},
|
||||
|
||||
rendering: {},
|
||||
debug: require("./config.local").default,
|
||||
debug: G_IS_DEV ? require("./config.local").default : {},
|
||||
|
||||
// Secret vars
|
||||
info: {
|
||||
|
@ -2,98 +2,98 @@ export default {
|
||||
// You can set any debug options here!
|
||||
/* dev:start */
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Quickly enters the game and skips the main menu - good for fast iterating
|
||||
// fastGameEnter: true,
|
||||
_fastGameEnter: "Quickly enters the game and skips the main menu - good for fast iterating",
|
||||
fastGameEnter: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Skips any delays like transitions between states and such
|
||||
// noArtificialDelays: true,
|
||||
_noArtificialDelays: "Skips any delays like transitions between states and such",
|
||||
noArtificialDelays: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Disables writing of savegames, useful for testing the same savegame over and over
|
||||
// disableSavegameWrite: true,
|
||||
_disableSavegameWrite: "Disables writing of savegames, useful for testing the same savegame over and over",
|
||||
disableSavegameWrite: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Shows bounds of all entities
|
||||
// showEntityBounds: true,
|
||||
_showEntityBounds: "Shows bounds of all entities",
|
||||
showEntityBounds: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Shows arrows for every ejector / acceptor
|
||||
// showAcceptorEjectors: true,
|
||||
_showAcceptorEjectors: "Shows arrows for every ejector / acceptor",
|
||||
showAcceptorEjectors: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Disables the music (Overrides any setting, can cause weird behaviour)
|
||||
// disableMusic: true,
|
||||
_disableMusic: "Disables the music (Overrides any setting, can cause weird behaviour)",
|
||||
disableMusic: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Do not render static map entities (=most buildings)
|
||||
// doNotRenderStatics: true,
|
||||
_doNotRenderStatics: "Do not render static map entities (=most buildings)",
|
||||
doNotRenderStatics: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Allow to zoom freely without limits
|
||||
// disableZoomLimits: true,
|
||||
_disableZoomLimits: "Allow to zoom freely without limits",
|
||||
disableZoomLimits: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Shows a border arround every chunk
|
||||
// showChunkBorders: true,
|
||||
_showChunkBorders: "Shows a border arround every chunk",
|
||||
showChunkBorders: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// All rewards can be unlocked by passing just 1 of any shape
|
||||
// rewardsInstant: true,
|
||||
_rewardsInstant: "All rewards can be unlocked by passing just 1 of any shape",
|
||||
rewardsInstant: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Unlocks all buildings
|
||||
// allBuildingsUnlocked: true,
|
||||
_allBuildingsUnlocked: "Unlocks all buildings",
|
||||
allBuildingsUnlocked: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Disables cost of blueprints
|
||||
// blueprintsNoCost: true,
|
||||
_blueprintsNoCost: "Disables cost of blueprints",
|
||||
blueprintsNoCost: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Disables cost of upgrades
|
||||
// upgradesNoCost: true,
|
||||
_upgradesNoCost: "Disables cost of upgrades",
|
||||
upgradesNoCost: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Disables the dialog when completing a level
|
||||
// disableUnlockDialog: true,
|
||||
_disableUnlockDialog: "Disables the dialog when completing a level",
|
||||
disableUnlockDialog: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Disables the simulation - This effectively pauses the game.
|
||||
// disableLogicTicks: true,
|
||||
_disableLogicTicks: "Disables the simulation - This effectively pauses the game.",
|
||||
disableLogicTicks: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Test the rendering if everything is clipped out properly
|
||||
// testClipping: true,
|
||||
_testClipping: "Test the rendering if everything is clipped out properly",
|
||||
testClipping: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Allows to render slower, useful for recording at half speed to avoid stuttering
|
||||
// framePausesBetweenTicks: 250,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Replace all translations with emojis to see which texts are translateable
|
||||
// testTranslations: true,
|
||||
_testTranslations: "Replace all translations with emojis to see which texts are translateable",
|
||||
testTranslations: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Enables an inspector which shows information about the entity below the curosr
|
||||
// enableEntityInspector: true,
|
||||
_enableEntityInspector: "Enables an inspector which shows information about the entity below the curosr",
|
||||
enableEntityInspector: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Enables ads in the local build (normally they are deactivated there)
|
||||
// testAds: true,
|
||||
_testAds: "Enables ads in the local build (normally they are deactivated there)",
|
||||
testAds: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Disables the automatic switch to an overview when zooming out
|
||||
// disableMapOverview: true,
|
||||
_disableMapOverview: "Disables the automatic switch to an overview when zooming out",
|
||||
disableMapOverview: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Disables the notification when there are new entries in the changelog since last played
|
||||
// disableUpgradeNotification: true,
|
||||
_disableUpgradeNotification: "Disables the notification when there are new entries in the changelog since last played",
|
||||
disableUpgradeNotification: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Makes belts almost infinitely fast
|
||||
// instantBelts: true,
|
||||
_instantBelts: "Makes belts almost infinitely fast",
|
||||
instantBelts: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Makes item processors almost infinitely fast
|
||||
// instantProcessors: true,
|
||||
_instantProcessors: "Makes item processors almost infinitely fast",
|
||||
instantProcessors: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Makes miners almost infinitely fast
|
||||
// instantMiners: true,
|
||||
_instantMiners: "Makes miners almost infinitely fast",
|
||||
instantMiners: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// When using fastGameEnter, controls whether a new game is started or the last one is resumed
|
||||
// resumeGameOnFastEnter: true,
|
||||
_resumeGameOnFastEnter: "When using fastGameEnter, controls whether a new game is started or the last one is resumed",
|
||||
resumeGameOnFastEnter: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Special option used to render the trailer
|
||||
// renderForTrailer: true,
|
||||
_renderForTrailer: "Special option used to render the trailer",
|
||||
renderForTrailer: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Whether to render changes
|
||||
// renderChanges: true,
|
||||
_renderChanges: "Whether to render changes",
|
||||
renderChanges: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Whether to render belt paths
|
||||
// renderBeltPaths: true,
|
||||
_renderBeltPaths: "Whether to render belt paths",
|
||||
renderBeltPaths: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Whether to check belt paths
|
||||
// checkBeltPaths: true,
|
||||
_checkBeltPaths: "Whether to check belt paths",
|
||||
checkBeltPaths: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Whether to items / s instead of items / m in stats
|
||||
// detailedStatistics: true,
|
||||
_detailedStatistics: "Whether to items / s instead of items / m in stats",
|
||||
detailedStatistics: false,
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Shows detailed information about which atlas is used
|
||||
// showAtlasInfo: true,
|
||||
|
@ -73,6 +73,22 @@ export class TextualGameState extends GameState {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* content.
|
||||
|
@ -19,7 +19,7 @@ import { Vector } from "../core/vector";
|
||||
|
||||
/**
|
||||
* 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 = {
|
||||
// Set later
|
||||
@ -27,7 +27,7 @@ export const gBuildingVariants = {
|
||||
|
||||
/**
|
||||
* Registers a new variant
|
||||
* @param {number} id
|
||||
* @param {number | string} id
|
||||
* @param {typeof MetaBuilding} meta
|
||||
* @param {string} variant
|
||||
* @param {number} rotationVariant
|
||||
@ -50,7 +50,7 @@ export function registerBuildingVariant(
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} code
|
||||
* @param {number | string} code
|
||||
* @returns {BuildingVariantIdentifier}
|
||||
*/
|
||||
export function getBuildingDataFromCode(code) {
|
||||
@ -72,12 +72,12 @@ export function getCodeFromBuildingData(metaBuilding, variant, rotationVariant)
|
||||
data.variant === variant &&
|
||||
data.rotationVariant === rotationVariant
|
||||
) {
|
||||
return +key;
|
||||
return key;
|
||||
}
|
||||
}
|
||||
assertAlways(
|
||||
false,
|
||||
"Building not found by data: " + metaBuilding.getId() + " / " + variant + " / " + rotationVariant
|
||||
);
|
||||
return 0;
|
||||
return "0";
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ export function initComponentRegistry() {
|
||||
|
||||
assert(
|
||||
// @ts-ignore
|
||||
require.context("./components", false, /.*\.js/i).keys().length ===
|
||||
require.context("./components", false, /.*\.js/i).keys().length >=
|
||||
gComponentRegistry.getNumEntries(),
|
||||
"Not all components are registered"
|
||||
);
|
||||
|
@ -19,7 +19,7 @@ export class StaticMapEntityComponent extends Component {
|
||||
originalRotation: types.float,
|
||||
|
||||
// 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 {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.code Building code
|
||||
* @param {(number | string)=} param0.code Building code
|
||||
*/
|
||||
constructor({
|
||||
origin = new Vector(),
|
||||
@ -96,7 +96,7 @@ export class StaticMapEntityComponent extends Component {
|
||||
|
||||
this.origin = origin;
|
||||
this.rotation = rotation;
|
||||
this.code = code;
|
||||
this.code = code.toString();
|
||||
this.originalRotation = originalRotation;
|
||||
}
|
||||
|
||||
|
@ -189,7 +189,7 @@ export class Entity extends BasicSerializableObject {
|
||||
const acceptorComp = this.components.ItemAcceptor;
|
||||
|
||||
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) {
|
||||
const slot = acceptorComp.slots[i];
|
||||
const slotTile = staticComp.localTileToWorld(slot.pos);
|
||||
|
@ -22,6 +22,7 @@ import { LeverSystem } from "./systems/lever";
|
||||
import { DisplaySystem } from "./systems/display";
|
||||
import { ItemProcessorOverlaysSystem } from "./systems/item_processor_overlays";
|
||||
import { BeltReaderSystem } from "./systems/belt_reader";
|
||||
import { ModSystems, logger as GeoZLogger } from "../GeoZ/main";
|
||||
|
||||
const logger = createLogger("game_system_manager");
|
||||
|
||||
@ -153,6 +154,55 @@ export class GameSystemManager {
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
|
@ -2,11 +2,13 @@ import { globalConfig } from "../core/config";
|
||||
import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils";
|
||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
import { enumColors } from "./colors";
|
||||
import { allShapeData } from "./shapes";
|
||||
import { enumItemProcessorTypes } from "./components/item_processor";
|
||||
import { GameRoot } from "./root";
|
||||
import { enumSubShape, ShapeDefinition } from "./shape_definition";
|
||||
import { ShapeDefinition, ShapeLayer } from "./shape_definition";
|
||||
import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals";
|
||||
import { UPGRADES } from "./upgrades";
|
||||
import { ModProcessors } from "../GeoZ/main";
|
||||
|
||||
export class HubGoals extends BasicSerializableObject {
|
||||
static getId() {
|
||||
@ -324,16 +326,16 @@ export class HubGoals extends BasicSerializableObject {
|
||||
*/
|
||||
createRandomShape() {
|
||||
const layerCount = clamp(this.level / 25, 2, 4);
|
||||
/** @type {Array<import("./shape_definition").ShapeLayer>} */
|
||||
/** @type {Array<ShapeLayer>} */
|
||||
let layers = [];
|
||||
|
||||
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;
|
||||
|
||||
for (let i = 0; i < layerCount; ++i) {
|
||||
/** @type {import("./shape_definition").ShapeLayer} */
|
||||
/** @type {ShapeLayer} */
|
||||
const layer = [null, null, null, null];
|
||||
|
||||
for (let quad = 0; quad < 4; ++quad) {
|
||||
@ -439,8 +441,17 @@ export class HubGoals extends BasicSerializableObject {
|
||||
globalConfig.buildingSpeeds[processorType]
|
||||
);
|
||||
}
|
||||
default:
|
||||
default: {
|
||||
if (ModProcessors[processorType]) {
|
||||
return (
|
||||
globalConfig.beltSpeedItemsPerSecond *
|
||||
this.upgradeImprovements.processors *
|
||||
globalConfig.buildingSpeeds[processorType]
|
||||
);
|
||||
}
|
||||
|
||||
assertAlways(false, "invalid processor type: " + processorType);
|
||||
}
|
||||
}
|
||||
|
||||
return 1 / globalConfig.beltSpeedItemsPerSecond;
|
||||
|
@ -13,8 +13,12 @@ import { MetaLeverBuilding } from "../../buildings/lever";
|
||||
import { MetaFilterBuilding } from "../../buildings/filter";
|
||||
import { MetaDisplayBuilding } from "../../buildings/display";
|
||||
import { MetaReaderBuilding } from "../../buildings/reader";
|
||||
import { MetaBuilding } from "../../meta_building";
|
||||
|
||||
const supportedBuildings = [
|
||||
/**
|
||||
* @type {Array<typeof MetaBuilding>}
|
||||
*/
|
||||
export const supportedBuildings = [
|
||||
MetaBeltBaseBuilding,
|
||||
MetaSplitterBuilding,
|
||||
MetaUndergroundBeltBuilding,
|
||||
|
@ -7,7 +7,7 @@ import { Application } from "../application";
|
||||
import { Signal, STOP_PROPAGATION } from "../core/signal";
|
||||
import { IS_MOBILE } from "../core/config";
|
||||
import { T } from "../translations";
|
||||
function key(str) {
|
||||
export function key(str) {
|
||||
return str.toUpperCase().charCodeAt(0);
|
||||
}
|
||||
|
||||
|
@ -5,10 +5,11 @@ import { clamp, fastArrayDeleteValueIfContained, make2DUndefinedArray } from "..
|
||||
import { Vector } from "../core/vector";
|
||||
import { BaseItem } from "./base_item";
|
||||
import { enumColors } from "./colors";
|
||||
import { allShapeData } from "./shapes";
|
||||
import { Entity } from "./entity";
|
||||
import { COLOR_ITEM_SINGLETONS } from "./items/color_item";
|
||||
import { GameRoot } from "./root";
|
||||
import { enumSubShape } from "./shape_definition";
|
||||
import { enumSubShape } from "./shapes";
|
||||
import { Rectangle } from "../core/rectangle";
|
||||
|
||||
const logger = createLogger("map_chunk");
|
||||
@ -180,56 +181,58 @@ export class MapChunk {
|
||||
*/
|
||||
internalGenerateShapePatch(rng, shapePatchSize, distanceToOriginInChunks) {
|
||||
/** @type {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} */
|
||||
let subShapes = null;
|
||||
let quads = null;
|
||||
|
||||
let weights = {};
|
||||
|
||||
// Later there is a mix of everything
|
||||
weights = {
|
||||
[enumSubShape.rect]: 100,
|
||||
[enumSubShape.circle]: Math.round(50 + clamp(distanceToOriginInChunks * 2, 0, 50)),
|
||||
[enumSubShape.star]: Math.round(20 + clamp(distanceToOriginInChunks, 0, 30)),
|
||||
[enumSubShape.windmill]: Math.round(6 + clamp(distanceToOriginInChunks / 2, 0, 20)),
|
||||
};
|
||||
|
||||
if (distanceToOriginInChunks < 7) {
|
||||
// Initial chunks can not spawn the good stuff
|
||||
weights[enumSubShape.star] = 0;
|
||||
weights[enumSubShape.windmill] = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
for (let s in allShapeData) {
|
||||
const data = allShapeData[s];
|
||||
if (!data.spawnData || distanceToOriginInChunks < data.spawnData.minDistance) {
|
||||
continue;
|
||||
}
|
||||
const chances = data.spawnData.chances;
|
||||
const chance = Math.round(
|
||||
clamp(
|
||||
chances.min + (distanceToOriginInChunks - data.spawnData.minDistance) * chances.distanceMultiplier,
|
||||
0,
|
||||
chances.max
|
||||
)
|
||||
);
|
||||
if (chance) {
|
||||
weights[data.id] = chance;
|
||||
}
|
||||
}
|
||||
if (windmillCount > 1) {
|
||||
subShapes[0] = enumSubShape.rect;
|
||||
subShapes[1] = enumSubShape.rect;
|
||||
quads = [
|
||||
this.internalGenerateRandomSubShape(rng, weights),
|
||||
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(
|
||||
rng,
|
||||
shapePatchSize,
|
||||
|
@ -6,6 +6,7 @@ import { Vector } from "../core/vector";
|
||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
import { enumColors, enumColorsToHexCode, enumColorToShortcode, enumShortcodeToColor } from "./colors";
|
||||
import { THEME } from "./theme";
|
||||
import { allShapeData, ShapeData, enumShortcodeToSubShape, enumSubShapeToShortcode, enumSubShape } from "./shapes";
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
@ -26,28 +27,6 @@ const arrayQuadrantIndexToOffset = [
|
||||
new Vector(-1, -1), // tl
|
||||
];
|
||||
|
||||
/** @enum {string} */
|
||||
export const enumSubShape = {
|
||||
rect: "rect",
|
||||
circle: "circle",
|
||||
star: "star",
|
||||
windmill: "windmill",
|
||||
};
|
||||
|
||||
/** @enum {string} */
|
||||
export const enumSubShapeToShortcode = {
|
||||
[enumSubShape.rect]: "R",
|
||||
[enumSubShape.circle]: "C",
|
||||
[enumSubShape.star]: "S",
|
||||
[enumSubShape.windmill]: "W",
|
||||
};
|
||||
|
||||
/** @enum {enumSubShape} */
|
||||
export const enumShortcodeToSubShape = {};
|
||||
for (const key in enumSubShapeToShortcode) {
|
||||
enumShortcodeToSubShape[enumSubShapeToShortcode[key]] = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given parameters to a valid shape definition
|
||||
* @param {*} layers
|
||||
@ -85,6 +64,7 @@ export class ShapeDefinition extends BasicSerializableObject {
|
||||
return errorCode;
|
||||
}
|
||||
const definition = ShapeDefinition.fromShortKey(data);
|
||||
/** @type {Array<ShapeLayer>} */
|
||||
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) {
|
||||
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);
|
||||
|
||||
for (let quadrantIndex = 0; quadrantIndex < 4; ++quadrantIndex) {
|
||||
if (!quadrants[quadrantIndex]) {
|
||||
for (let quad of quads) {
|
||||
if (!quad) {
|
||||
continue;
|
||||
}
|
||||
const { subShape, color, quadrantIndex } = quad;
|
||||
if (subShape == "-") {
|
||||
continue;
|
||||
}
|
||||
const { subShape, color } = quadrants[quadrantIndex];
|
||||
|
||||
const quadrantPos = arrayQuadrantIndexToOffset[quadrantIndex];
|
||||
|
||||
const centerQuadrantX = quadrantPos.x * quadrantHalfSize;
|
||||
const centerQuadrantY = quadrantPos.y * quadrantHalfSize;
|
||||
|
||||
const rotation = Math.radians(quadrantIndex * 90);
|
||||
|
||||
context.save();
|
||||
context.translate(centerQuadrantX, centerQuadrantY);
|
||||
context.rotate(rotation);
|
||||
|
||||
context.fillStyle = enumColorsToHexCode[color];
|
||||
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;
|
||||
|
||||
switch (subShape) {
|
||||
case enumSubShape.rect: {
|
||||
context.beginPath();
|
||||
const dims = quadrantSize * layerScale;
|
||||
context.rect(
|
||||
insetPadding + -quadrantHalfSize,
|
||||
-insetPadding + quadrantHalfSize - dims,
|
||||
dims,
|
||||
dims
|
||||
);
|
||||
const dims = quadrantSize * layerScale;
|
||||
const innerDims = insetPadding - quadrantHalfSize;
|
||||
|
||||
break;
|
||||
let began = null;
|
||||
// eslint-disable-next-line no-inner-declarations
|
||||
/** @type {import("./shapes").BeginDrawShape} */
|
||||
function begin(args) {
|
||||
context.save();
|
||||
context.translate(innerDims, -innerDims);
|
||||
context.scale(dims, -dims);
|
||||
context.lineWidth = lineWidth / dims / (args.scale || 1);
|
||||
if (args.scale) {
|
||||
context.scale(args.scale, args.scale);
|
||||
}
|
||||
case enumSubShape.star: {
|
||||
if (args.beginPath) {
|
||||
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 - moveInwards, originY + dims);
|
||||
context.lineTo(originX, originY + dims);
|
||||
}
|
||||
if (args.moveToZero) {
|
||||
context.moveTo(0, 0);
|
||||
}
|
||||
began = args;
|
||||
}
|
||||
// eslint-disable-next-line no-inner-declarations
|
||||
function end() {
|
||||
if (!began) {
|
||||
return;
|
||||
}
|
||||
if (began.path) {
|
||||
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();
|
||||
context.stroke();
|
||||
/** @type {ShapeData} */
|
||||
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.translate(-centerQuadrantX, -centerQuadrantY);
|
||||
context.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,8 @@ import { BasicSerializableObject } from "../savegame/serialization";
|
||||
import { enumColors } from "./colors";
|
||||
import { ShapeItem } from "./items/shape_item";
|
||||
import { GameRoot } from "./root";
|
||||
import { enumSubShape, ShapeDefinition } from "./shape_definition";
|
||||
import { ShapeDefinition } from "./shape_definition";
|
||||
import { enumSubShape } from "./shapes";
|
||||
|
||||
const logger = createLogger("shape_definition_manager");
|
||||
|
||||
@ -256,4 +257,18 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
|
||||
|
||||
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
181
src/js/game/shapes.js
Normal 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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -238,8 +238,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
const items = processorComp.inputSlots;
|
||||
processorComp.inputSlots = [];
|
||||
|
||||
/** @type {Object.<string, { item: BaseItem, sourceSlot: number }>} */
|
||||
const itemsBySlot = {};
|
||||
/** @type {Array<{ item: BaseItem, sourceSlot: number }>} */
|
||||
const itemsBySlot = [];
|
||||
for (let i = 0; i < items.length; ++i) {
|
||||
itemsBySlot[items[i].sourceSlot] = items[i];
|
||||
}
|
||||
@ -251,292 +251,309 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
let trackProduction = true;
|
||||
|
||||
// DO SOME MAGIC
|
||||
const ModProcessors = require("../../GeoZ/main").ModProcessors;
|
||||
if (ModProcessors[processorComp.type]) {
|
||||
trackProduction = ModProcessors[processorComp.type].process({
|
||||
items: itemsBySlot.map(e => e.item),
|
||||
itemsBySlot,
|
||||
itemsRaw: items,
|
||||
entity,
|
||||
outItems,
|
||||
system: this,
|
||||
});
|
||||
} else {
|
||||
switch (processorComp.type) {
|
||||
// SPLITTER
|
||||
case enumItemProcessorTypes.splitterWires:
|
||||
case enumItemProcessorTypes.splitter: {
|
||||
trackProduction = false;
|
||||
const availableSlots = entity.components.ItemEjector.slots.length;
|
||||
|
||||
switch (processorComp.type) {
|
||||
// SPLITTER
|
||||
case enumItemProcessorTypes.splitterWires:
|
||||
case enumItemProcessorTypes.splitter: {
|
||||
trackProduction = false;
|
||||
const availableSlots = entity.components.ItemEjector.slots.length;
|
||||
|
||||
let nextSlot = processorComp.nextOutputSlot++ % availableSlots;
|
||||
for (let i = 0; i < items.length; ++i) {
|
||||
outItems.push({
|
||||
item: items[i].item,
|
||||
preferredSlot: (nextSlot + i) % availableSlots,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// CUTTER
|
||||
case enumItemProcessorTypes.cutter: {
|
||||
const inputItem = /** @type {ShapeItem} */ (items[0].item);
|
||||
assert(inputItem instanceof ShapeItem, "Input for cut is not a shape");
|
||||
const inputDefinition = inputItem.definition;
|
||||
|
||||
const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutHalf(inputDefinition);
|
||||
|
||||
for (let i = 0; i < cutDefinitions.length; ++i) {
|
||||
const definition = cutDefinitions[i];
|
||||
if (!definition.isEntirelyEmpty()) {
|
||||
let nextSlot = processorComp.nextOutputSlot++ % availableSlots;
|
||||
for (let i = 0; i < items.length; ++i) {
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
|
||||
requiredSlot: i,
|
||||
item: items[i].item,
|
||||
preferredSlot: (nextSlot + i) % availableSlots,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
// CUTTER
|
||||
case enumItemProcessorTypes.cutter: {
|
||||
const inputItem = /** @type {ShapeItem} */ (items[0].item);
|
||||
assert(inputItem instanceof ShapeItem, "Input for cut is not a shape");
|
||||
const inputDefinition = inputItem.definition;
|
||||
|
||||
// CUTTER (Quad)
|
||||
case enumItemProcessorTypes.cutterQuad: {
|
||||
const inputItem = /** @type {ShapeItem} */ (items[0].item);
|
||||
assert(inputItem instanceof ShapeItem, "Input for cut is not a shape");
|
||||
const inputDefinition = inputItem.definition;
|
||||
const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutHalf(inputDefinition);
|
||||
|
||||
const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutQuad(inputDefinition);
|
||||
|
||||
for (let i = 0; i < cutDefinitions.length; ++i) {
|
||||
const definition = cutDefinitions[i];
|
||||
if (!definition.isEntirelyEmpty()) {
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
|
||||
requiredSlot: i,
|
||||
});
|
||||
for (let i = 0; i < cutDefinitions.length; ++i) {
|
||||
const definition = cutDefinitions[i];
|
||||
if (!definition.isEntirelyEmpty()) {
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
|
||||
requiredSlot: i,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
// CUTTER (Quad)
|
||||
case enumItemProcessorTypes.cutterQuad: {
|
||||
const inputItem = /** @type {ShapeItem} */ (items[0].item);
|
||||
assert(inputItem instanceof ShapeItem, "Input for cut is not a shape");
|
||||
const inputDefinition = inputItem.definition;
|
||||
|
||||
// ROTATER
|
||||
case enumItemProcessorTypes.rotater: {
|
||||
const inputItem = /** @type {ShapeItem} */ (items[0].item);
|
||||
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
|
||||
const inputDefinition = inputItem.definition;
|
||||
const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutQuad(inputDefinition);
|
||||
|
||||
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(inputDefinition);
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// ROTATER (CCW)
|
||||
case enumItemProcessorTypes.rotaterCCW: {
|
||||
const inputItem = /** @type {ShapeItem} */ (items[0].item);
|
||||
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
|
||||
const inputDefinition = inputItem.definition;
|
||||
|
||||
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCCW(inputDefinition);
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// ROTATER (FL)
|
||||
case enumItemProcessorTypes.rotaterFL: {
|
||||
const inputItem = /** @type {ShapeItem} */ (items[0].item);
|
||||
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
|
||||
const inputDefinition = inputItem.definition;
|
||||
|
||||
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateFL(inputDefinition);
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// STACKER
|
||||
|
||||
case enumItemProcessorTypes.stacker: {
|
||||
const lowerItem = /** @type {ShapeItem} */ (itemsBySlot[0].item);
|
||||
const upperItem = /** @type {ShapeItem} */ (itemsBySlot[1].item);
|
||||
|
||||
assert(lowerItem instanceof ShapeItem, "Input for lower stack is not a shape");
|
||||
assert(upperItem instanceof ShapeItem, "Input for upper stack is not a shape");
|
||||
|
||||
const stackedDefinition = this.root.shapeDefinitionMgr.shapeActionStack(
|
||||
lowerItem.definition,
|
||||
upperItem.definition
|
||||
);
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(stackedDefinition),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// TRASH
|
||||
|
||||
case enumItemProcessorTypes.trash: {
|
||||
// Well this one is easy .. simply do nothing with the item
|
||||
break;
|
||||
}
|
||||
|
||||
// MIXER
|
||||
|
||||
case enumItemProcessorTypes.mixer: {
|
||||
// Find both colors and combine them
|
||||
const item1 = /** @type {ColorItem} */ (items[0].item);
|
||||
const item2 = /** @type {ColorItem} */ (items[1].item);
|
||||
assert(item1 instanceof ColorItem, "Input for color mixer is not a color");
|
||||
assert(item2 instanceof ColorItem, "Input for color mixer is not a color");
|
||||
|
||||
const color1 = item1.color;
|
||||
const color2 = item2.color;
|
||||
|
||||
// Try finding mixer color, and if we can't mix it we simply return the same color
|
||||
const mixedColor = enumColorMixingResults[color1][color2];
|
||||
let resultColor = color1;
|
||||
if (mixedColor) {
|
||||
resultColor = mixedColor;
|
||||
}
|
||||
outItems.push({
|
||||
item: COLOR_ITEM_SINGLETONS[resultColor],
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// PAINTER
|
||||
|
||||
case enumItemProcessorTypes.painter: {
|
||||
const shapeItem = /** @type {ShapeItem} */ (itemsBySlot[0].item);
|
||||
const colorItem = /** @type {ColorItem} */ (itemsBySlot[1].item);
|
||||
|
||||
const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith(
|
||||
shapeItem.definition,
|
||||
colorItem.color
|
||||
);
|
||||
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition),
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// PAINTER (DOUBLE)
|
||||
|
||||
case enumItemProcessorTypes.painterDouble: {
|
||||
const shapeItem1 = /** @type {ShapeItem} */ (itemsBySlot[0].item);
|
||||
const shapeItem2 = /** @type {ShapeItem} */ (itemsBySlot[1].item);
|
||||
const colorItem = /** @type {ColorItem} */ (itemsBySlot[2].item);
|
||||
|
||||
assert(shapeItem1 instanceof ShapeItem, "Input for painter is not a shape");
|
||||
assert(shapeItem2 instanceof ShapeItem, "Input for painter is not a shape");
|
||||
assert(colorItem instanceof ColorItem, "Input for painter is not a color");
|
||||
|
||||
const colorizedDefinition1 = this.root.shapeDefinitionMgr.shapeActionPaintWith(
|
||||
shapeItem1.definition,
|
||||
colorItem.color
|
||||
);
|
||||
|
||||
const colorizedDefinition2 = this.root.shapeDefinitionMgr.shapeActionPaintWith(
|
||||
shapeItem2.definition,
|
||||
colorItem.color
|
||||
);
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition1),
|
||||
});
|
||||
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition2),
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// PAINTER (QUAD)
|
||||
|
||||
case enumItemProcessorTypes.painterQuad: {
|
||||
const shapeItem = /** @type {ShapeItem} */ (itemsBySlot[0].item);
|
||||
assert(shapeItem instanceof ShapeItem, "Input for painter is not a shape");
|
||||
|
||||
/** @type {Array<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;
|
||||
for (let i = 0; i < cutDefinitions.length; ++i) {
|
||||
const definition = cutDefinitions[i];
|
||||
if (!definition.isEntirelyEmpty()) {
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
|
||||
requiredSlot: i,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith4Colors(
|
||||
shapeItem.definition,
|
||||
/** @type {[string, string, string, string]} */ (colors)
|
||||
);
|
||||
// ROTATER
|
||||
case enumItemProcessorTypes.rotater: {
|
||||
const inputItem = /** @type {ShapeItem} */ (items[0].item);
|
||||
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
|
||||
const inputDefinition = inputItem.definition;
|
||||
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// FILTER
|
||||
case enumItemProcessorTypes.filter: {
|
||||
// TODO
|
||||
trackProduction = false;
|
||||
|
||||
const item = itemsBySlot[0].item;
|
||||
|
||||
const network = entity.components.WiredPins.slots[0].linkedNetwork;
|
||||
if (!network || !network.currentValue) {
|
||||
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(
|
||||
inputDefinition
|
||||
);
|
||||
outItems.push({
|
||||
item,
|
||||
requiredSlot: 1,
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
const value = network.currentValue;
|
||||
if (value.equals(BOOL_TRUE_SINGLETON) || value.equals(item)) {
|
||||
// ROTATER (CCW)
|
||||
case enumItemProcessorTypes.rotaterCCW: {
|
||||
const inputItem = /** @type {ShapeItem} */ (items[0].item);
|
||||
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
|
||||
const inputDefinition = inputItem.definition;
|
||||
|
||||
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCCW(
|
||||
inputDefinition
|
||||
);
|
||||
outItems.push({
|
||||
item,
|
||||
requiredSlot: 0,
|
||||
});
|
||||
} else {
|
||||
outItems.push({
|
||||
item,
|
||||
requiredSlot: 1,
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
// ROTATER (FL)
|
||||
case enumItemProcessorTypes.rotaterFL: {
|
||||
const inputItem = /** @type {ShapeItem} */ (items[0].item);
|
||||
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
|
||||
const inputDefinition = inputItem.definition;
|
||||
|
||||
// READER
|
||||
case enumItemProcessorTypes.reader: {
|
||||
// Pass through the item
|
||||
const item = itemsBySlot[0].item;
|
||||
outItems.push({ item });
|
||||
|
||||
// Track the item
|
||||
const readerComp = entity.components.BeltReader;
|
||||
readerComp.lastItemTimes.push(this.root.time.now());
|
||||
readerComp.lastItem = item;
|
||||
break;
|
||||
}
|
||||
|
||||
// HUB
|
||||
case enumItemProcessorTypes.hub: {
|
||||
trackProduction = false;
|
||||
|
||||
const hubComponent = entity.components.Hub;
|
||||
assert(hubComponent, "Hub item processor has no hub component");
|
||||
|
||||
for (let i = 0; i < items.length; ++i) {
|
||||
const item = /** @type {ShapeItem} */ (items[i].item);
|
||||
this.root.hubGoals.handleDefinitionDelivered(item.definition);
|
||||
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateFL(
|
||||
inputDefinition
|
||||
);
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
// STACKER
|
||||
|
||||
default:
|
||||
assertAlways(false, "Unkown item processor type: " + processorComp.type);
|
||||
case enumItemProcessorTypes.stacker: {
|
||||
const lowerItem = /** @type {ShapeItem} */ (itemsBySlot[0].item);
|
||||
const upperItem = /** @type {ShapeItem} */ (itemsBySlot[1].item);
|
||||
|
||||
assert(lowerItem instanceof ShapeItem, "Input for lower stack is not a shape");
|
||||
assert(upperItem instanceof ShapeItem, "Input for upper stack is not a shape");
|
||||
|
||||
const stackedDefinition = this.root.shapeDefinitionMgr.shapeActionStack(
|
||||
lowerItem.definition,
|
||||
upperItem.definition
|
||||
);
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(stackedDefinition),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// TRASH
|
||||
|
||||
case enumItemProcessorTypes.trash: {
|
||||
// Well this one is easy .. simply do nothing with the item
|
||||
break;
|
||||
}
|
||||
|
||||
// MIXER
|
||||
|
||||
case enumItemProcessorTypes.mixer: {
|
||||
// Find both colors and combine them
|
||||
const item1 = /** @type {ColorItem} */ (items[0].item);
|
||||
const item2 = /** @type {ColorItem} */ (items[1].item);
|
||||
assert(item1 instanceof ColorItem, "Input for color mixer is not a color");
|
||||
assert(item2 instanceof ColorItem, "Input for color mixer is not a color");
|
||||
|
||||
const color1 = item1.color;
|
||||
const color2 = item2.color;
|
||||
|
||||
// Try finding mixer color, and if we can't mix it we simply return the same color
|
||||
const mixedColor = enumColorMixingResults[color1][color2];
|
||||
let resultColor = color1;
|
||||
if (mixedColor) {
|
||||
resultColor = mixedColor;
|
||||
}
|
||||
outItems.push({
|
||||
item: COLOR_ITEM_SINGLETONS[resultColor],
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// PAINTER
|
||||
|
||||
case enumItemProcessorTypes.painter: {
|
||||
const shapeItem = /** @type {ShapeItem} */ (itemsBySlot[0].item);
|
||||
const colorItem = /** @type {ColorItem} */ (itemsBySlot[1].item);
|
||||
|
||||
const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith(
|
||||
shapeItem.definition,
|
||||
colorItem.color
|
||||
);
|
||||
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition),
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// PAINTER (DOUBLE)
|
||||
|
||||
case enumItemProcessorTypes.painterDouble: {
|
||||
const shapeItem1 = /** @type {ShapeItem} */ (itemsBySlot[0].item);
|
||||
const shapeItem2 = /** @type {ShapeItem} */ (itemsBySlot[1].item);
|
||||
const colorItem = /** @type {ColorItem} */ (itemsBySlot[2].item);
|
||||
|
||||
assert(shapeItem1 instanceof ShapeItem, "Input for painter is not a shape");
|
||||
assert(shapeItem2 instanceof ShapeItem, "Input for painter is not a shape");
|
||||
assert(colorItem instanceof ColorItem, "Input for painter is not a color");
|
||||
|
||||
const colorizedDefinition1 = this.root.shapeDefinitionMgr.shapeActionPaintWith(
|
||||
shapeItem1.definition,
|
||||
colorItem.color
|
||||
);
|
||||
|
||||
const colorizedDefinition2 = this.root.shapeDefinitionMgr.shapeActionPaintWith(
|
||||
shapeItem2.definition,
|
||||
colorItem.color
|
||||
);
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition1),
|
||||
});
|
||||
|
||||
outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition2),
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// PAINTER (QUAD)
|
||||
|
||||
case enumItemProcessorTypes.painterQuad: {
|
||||
const shapeItem = /** @type {ShapeItem} */ (itemsBySlot[0].item);
|
||||
assert(shapeItem instanceof ShapeItem, "Input for painter is not a shape");
|
||||
|
||||
/** @type {Array<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
|
||||
|
@ -25,6 +25,12 @@ export class LogicGateSystem extends GameSystemWithFilter {
|
||||
[enumLogicGateType.unstacker]: this.compute_UNSTACK.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() {
|
||||
|
@ -10,6 +10,7 @@ import { initDrawUtils } from "./core/draw_utils";
|
||||
import { initItemRegistry } from "./game/item_registry";
|
||||
import { initMetaBuildingRegistry } from "./game/meta_building_registry";
|
||||
import { initGameSpeedRegistry } from "./game/game_speed_registry";
|
||||
import { initMods } from "./GeoZ/main";
|
||||
|
||||
const logger = createLogger("main");
|
||||
|
||||
@ -18,37 +19,6 @@ if (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(
|
||||
`%cshapez.io ️%c\n© 2020 Tobias Springer IT Solutions\nCommit %c${G_BUILD_COMMIT_HASH}%c on %c${new Date(
|
||||
G_BUILD_TIME
|
||||
@ -77,12 +47,6 @@ console.log("%cDEVCODE BUILT IN", "color: #f77");
|
||||
|
||||
logSection("Boot Process", "#f9a825");
|
||||
|
||||
initDrawUtils();
|
||||
initComponentRegistry();
|
||||
initItemRegistry();
|
||||
initMetaBuildingRegistry();
|
||||
initGameSpeedRegistry();
|
||||
|
||||
let app = null;
|
||||
|
||||
function bootApp() {
|
||||
|
@ -10,6 +10,7 @@ import { THEMES, THEME, applyGameTheme } from "../game/theme";
|
||||
import { IS_DEMO } from "../core/config";
|
||||
import { T } from "../translations";
|
||||
import { LANGUAGES } from "../languages";
|
||||
import { globalConfig, IS_DEBUG } from "../core/config";
|
||||
|
||||
const logger = createLogger("application_settings");
|
||||
|
||||
@ -21,6 +22,8 @@ export const enumCategories = {
|
||||
userInterface: "userInterface",
|
||||
performance: "performance",
|
||||
advanced: "advanced",
|
||||
debug: "debug",
|
||||
keybindings: "keybindings",
|
||||
};
|
||||
|
||||
export const uiScales = [
|
||||
@ -278,6 +281,21 @@ export const allApplicationSettings = [
|
||||
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) {
|
||||
return allApplicationSettings.find(setting => setting.id === id);
|
||||
}
|
||||
@ -358,7 +376,9 @@ export class ApplicationSettings extends ReadWriteProxy {
|
||||
* @param {string} 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];
|
||||
}
|
||||
|
||||
|
@ -185,6 +185,10 @@ export class BoolSetting extends BaseSetting {
|
||||
}
|
||||
|
||||
getHtml() {
|
||||
if (!T.settings.labels[this.id].description) {
|
||||
let a = T;
|
||||
let b = a;
|
||||
}
|
||||
return `
|
||||
<div class="setting cardbox ${this.enabled ? "enabled" : "disabled"}">
|
||||
${this.enabled ? "" : `<span class="standaloneOnlyHint">${T.demo.settingNotAvailable}</span>`}
|
||||
|
@ -4,39 +4,58 @@ import { T } from "../translations";
|
||||
import { KEYMAPPINGS, getStringForKeyCode } from "../game/key_action_mapper";
|
||||
import { Dialog } from "../core/modal_dialog_elements";
|
||||
import { IS_DEMO } from "../core/config";
|
||||
import { SettingsState } from "./settings";
|
||||
|
||||
export class KeybindingsState extends TextualGameState {
|
||||
constructor() {
|
||||
super("KeybindingsState");
|
||||
}
|
||||
export class KeybindingsState extends SettingsState {
|
||||
// constructor() {
|
||||
// super();
|
||||
// super("KeybindingsState");
|
||||
// this.settingsState = settingsState;
|
||||
// }
|
||||
|
||||
getStateHeaderTitle() {
|
||||
return T.keybindings.title;
|
||||
}
|
||||
// getStateHeaderTitle() {
|
||||
// return T.keybindings.title;
|
||||
// }
|
||||
|
||||
getMainContentHTML() {
|
||||
return `
|
||||
<div class="sidebar">
|
||||
${this.getCategoryButtonsHtml()}
|
||||
|
||||
<div class="topEntries">
|
||||
<span class="hint">${T.keybindings.hint}</span>
|
||||
<button class="styledButton resetBindings">${T.keybindings.resetKeybindings}</button>
|
||||
<div class="other">
|
||||
<button class="styledButton about">${T.about.title}</button>
|
||||
|
||||
<div class="versionbar">
|
||||
<div class="buildVersion">${T.global.loading} ...</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>
|
||||
`;
|
||||
}
|
||||
|
||||
onEnter() {
|
||||
this.onEnterCommon();
|
||||
|
||||
const keybindingsElem = this.htmlElement.querySelector(".keybindings");
|
||||
|
||||
this.trackClicks(this.htmlElement.querySelector(".resetBindings"), this.resetBindings);
|
||||
|
||||
for (const category in KEYMAPPINGS) {
|
||||
const categoryDiv = document.createElement("div");
|
||||
categoryDiv.classList.add("category");
|
||||
categoryDiv.classList.add("keyCategory");
|
||||
keybindingsElem.appendChild(categoryDiv);
|
||||
|
||||
const labelDiv = document.createElement("strong");
|
||||
@ -173,7 +192,7 @@ export class KeybindingsState extends TextualGameState {
|
||||
});
|
||||
}
|
||||
|
||||
getDefaultPreviousState() {
|
||||
return "SettingsState";
|
||||
}
|
||||
// getDefaultPreviousState() {
|
||||
// return "SettingsState";
|
||||
// }
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ export class MainMenuState extends GameState {
|
||||
|
||||
<div class="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>
|
||||
|
||||
|
||||
@ -119,7 +119,7 @@ export class MainMenuState extends GameState {
|
||||
|
||||
<div class="author">${T.mainMenu.madeBy.replace(
|
||||
"<author-link>",
|
||||
'<a class="producerLink" target="_blank">Tobias Springer</a>'
|
||||
'<a class="producerLink" target="_blank">Tobias Springer & modded by Exund</a>'
|
||||
)}</div>
|
||||
|
||||
</div>
|
||||
|
@ -8,6 +8,12 @@ import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
|
||||
import { CHANGELOG } from "../changelog";
|
||||
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");
|
||||
|
||||
export class PreloadState extends GameState {
|
||||
@ -108,6 +114,18 @@ export class PreloadState extends GameState {
|
||||
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(() => {
|
||||
// Initialize fullscreen
|
||||
if (this.app.platformWrapper.getSupportsFullscreen()) {
|
||||
|
@ -4,8 +4,8 @@ import { allApplicationSettings, enumCategories } from "../profile/application_s
|
||||
import { T } from "../translations";
|
||||
|
||||
export class SettingsState extends TextualGameState {
|
||||
constructor() {
|
||||
super("SettingsState");
|
||||
constructor(key = "SettingsState") {
|
||||
super(key);
|
||||
}
|
||||
|
||||
getStateHeaderTitle() {
|
||||
@ -92,7 +92,7 @@ export class SettingsState extends TextualGameState {
|
||||
</span>`;
|
||||
}
|
||||
|
||||
onEnter(payload) {
|
||||
onEnterCommon() {
|
||||
this.renderBuildText();
|
||||
this.trackClicks(this.htmlElement.querySelector(".about"), this.onAboutClicked, {
|
||||
preventDefault: false,
|
||||
@ -109,6 +109,12 @@ export class SettingsState extends TextualGameState {
|
||||
|
||||
this.htmlElement.querySelector(".category").classList.add("active");
|
||||
this.htmlElement.querySelector(".categoryButton").classList.add("active");
|
||||
|
||||
this.setActiveCategory(enumCategories.general);
|
||||
}
|
||||
|
||||
onEnter(payload) {
|
||||
this.onEnterCommon();
|
||||
}
|
||||
|
||||
setActiveCategory(category) {
|
||||
@ -164,6 +170,6 @@ export class SettingsState extends TextualGameState {
|
||||
}
|
||||
|
||||
onKeybindingsClicked() {
|
||||
this.moveToStateAddGoBack("KeybindingsState");
|
||||
this.switchToState("KeybindingsState");
|
||||
}
|
||||
}
|
||||
|
@ -727,6 +727,8 @@ settings:
|
||||
userInterface: User Interface
|
||||
advanced: Advanced
|
||||
performance: Performance
|
||||
debug: Debug
|
||||
keybindings: Keybindings
|
||||
|
||||
versionBadges:
|
||||
dev: Development
|
||||
|
Loading…
Reference in New Issue
Block a user