@minecraft/creator-tools
Version:
Minecraft Creator Tools command line and libraries.
423 lines (422 loc) • 15.2 kB
JavaScript
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const BlockRenderType_1 = require("./BlockRenderType");
const MinecraftUtilities_1 = __importDefault(require("./MinecraftUtilities"));
const Database_1 = __importDefault(require("./Database"));
const Utilities_1 = __importDefault(require("../core/Utilities"));
const ste_events_1 = require("ste-events");
const Log_1 = __importDefault(require("../core/Log"));
const ManagedComponent_1 = require("./ManagedComponent");
const StorageUtilities_1 = __importDefault(require("../storage/StorageUtilities"));
class BlockType {
data;
javaData;
_baseType;
_baseTypeId = "";
_isCustom = false;
_behaviorPackData = null;
_behaviorPackFile;
_blockResource;
_isLoaded = false;
_loadedWithComments = false;
_properties = {};
behaviorPackBlockTypeDef;
_managed = {};
_onLoaded = new ste_events_1.EventDispatcher();
_onComponentAdded = new ste_events_1.EventDispatcher();
_onComponentRemoved = new ste_events_1.EventDispatcher();
_onComponentChanged = new ste_events_1.EventDispatcher();
get numericId() {
return this.data.lid;
}
set numericId(newId) {
this.data.lid = newId;
}
get baseTypeId() {
return this._baseTypeId;
}
get mapColor() {
// First check if this specific block type has a map color
if (this.data.mc) {
return this.data.mc;
}
// Fall back to the base type's map color from mccat.json
if (this._baseType && this._baseType.mapColor) {
return this._baseType.mapColor;
}
// Check the default base type
const defaultBaseType = Database_1.default.defaultBlockBaseType;
if (defaultBaseType && defaultBaseType.mapColor) {
return defaultBaseType.mapColor;
}
return undefined;
}
get isCustom() {
return this._isCustom;
}
get baseType() {
if (this._baseType !== undefined) {
return this._baseType;
}
return Database_1.default.defaultBlockBaseType;
}
get catalogResource() {
if (this._blockResource) {
return this._blockResource;
}
if (Database_1.default.blocksCatalog) {
// First try with shortId (full block name like "acacia_button")
let lookupId = this.shortId;
if (lookupId) {
this._blockResource = Database_1.default.blocksCatalog.getCatalogResource(lookupId);
}
// Fall back to material if shortId lookup fails
if (!this._blockResource) {
let mat = this.material;
if (mat && mat !== lookupId) {
this._blockResource = Database_1.default.blocksCatalog.getCatalogResource(mat);
}
}
}
return this._blockResource;
}
set baseType(baseType) {
this._baseType = baseType;
this._baseTypeId = baseType.name;
}
get material() {
if (this.data.m) {
return this.data.m;
}
return this.data.n;
}
renderType = BlockRenderType_1.BlockRenderType.Custom;
get icon() {
let val = this.data.ic;
if (val === undefined && this.baseType !== undefined) {
val = this.baseType.icon;
}
return val;
}
get title() {
let id = this.shortId;
// For custom blocks, strip the namespace prefix (e.g., "ec_vv:bushy_cherry" -> "bushy_cherry")
const colonIndex = id.indexOf(":");
if (colonIndex >= 0) {
id = id.substring(colonIndex + 1);
}
return Utilities_1.default.humanifyMinecraftName(id);
}
get friendlyName() {
// First check if the base type has a friendly name from mccat.json
if (this._baseType && this._baseType.friendlyName) {
return this._baseType.friendlyName;
}
// Fall back to humanified shortId
return this.title;
}
constructor(typeId) {
this.javaData = null;
this.data = {
n: typeId,
};
if (typeId.indexOf(":") >= 0 && !typeId.startsWith("minecraft:")) {
this._isCustom = true;
}
}
getIcon() {
const icon = this.icon;
if (icon === undefined) {
return this.shortId;
}
const typeName = MinecraftUtilities_1.default.canonicalizeName(this.id);
const lastUnder = typeName.lastIndexOf("_");
let allButLast = typeName;
if (lastUnder >= 0) {
allButLast = allButLast.substring(0, lastUnder);
}
switch (icon) {
case "st_":
return "stone_" + allButLast;
case "le_":
return "leaves_acacia_carried";
case "pl_":
return "planks_" + allButLast;
case "_car":
return allButLast + "_carried";
case "plant_":
return typeName + "_fern_carried";
case "_":
return typeName;
case "_t":
return allButLast + "_top";
case "_br":
return allButLast + "_bricks";
case "_carr":
return typeName + "_carried";
case "_bl":
return allButLast + "_block";
case "_n":
return allButLast + "_normal";
case "_blt":
return allButLast + "_block_top";
case "mshblk":
const lastmb = allButLast.split("_");
return "mushroom_block_skin_" + lastmb[0];
case "ml_sm":
const lastml = allButLast.split("_");
return lastml[lastml.length - 2] + "_" + lastml[lastml.length - 1] + "_smooth";
case "l_blt":
const lastbt = allButLast.split("_");
return lastbt[lastbt.length - 1] + "_block_top";
case "|":
return allButLast;
case "|s":
return allButLast + "s";
case "l":
const last = allButLast.split("_");
return last[last.length - 1];
case "sb_":
const fu = allButLast.split("_");
return "stonebricks_" + fu[0];
case "st_l_sm":
const lasta = allButLast.split("_");
return "stone_" + lasta[lasta.length - 1] + "_smooth";
case "|rv":
// dark_prismarine_stairs => prismarine_dark_stairs
let result = "";
const rv = allButLast.split("_");
for (let i = rv.length - 1; i > 0; i--) {
if (result.length > 0) {
result += "_";
}
result += rv[i];
}
return result;
case "2rv":
// yellow_glazed_terracotta => glazed_terracotta_yellow
// dark_blue_glazed_terracotta => glazed_terracotta_dark_blue
let resultF = "";
const rvF = typeName.split("_");
if (rvF.length >= 2) {
resultF = rvF[rvF.length - 2] + "_" + rvF[rvF.length - 1];
for (let i = 0; i < rvF.length - 2; i++) {
resultF += "_" + rvF[i];
}
}
else {
resultF = typeName;
}
return resultF;
case "rv":
// dark_prismarine => prismarine_dark
let resultA = "";
const rvA = typeName.split("_");
for (let i = rvA.length - 1; i >= 0; i--) {
if (resultA.length > 0) {
resultA += "_";
}
resultA += rvA[i];
}
return resultA;
}
if (this.icon !== undefined) {
return this.icon;
}
return this.shortId;
}
get id() {
if (this.behaviorPackBlockTypeDef && this.behaviorPackBlockTypeDef.description) {
return this.behaviorPackBlockTypeDef.description.identifier;
}
if (this.data.n === undefined) {
return "";
}
if (this.data.n.endsWith("_")) {
return this.data.n + this.baseTypeId;
}
return this.data.n;
}
set id(newId) {
this.data.n = newId;
if (this.behaviorPackBlockTypeDef && this.behaviorPackBlockTypeDef.description && newId) {
this.behaviorPackBlockTypeDef.description.identifier = newId;
}
}
get onComponentAdded() {
return this._onComponentAdded.asEvent();
}
get onComponentRemoved() {
return this._onComponentRemoved.asEvent();
}
get onComponentChanged() {
return this._onComponentChanged.asEvent();
}
get isLoaded() {
return this._isLoaded;
}
get behaviorPackFile() {
return this._behaviorPackFile;
}
get onLoaded() {
return this._onLoaded.asEvent();
}
set behaviorPackFile(newFile) {
this._behaviorPackFile = newFile;
}
get shortId() {
let id = this.id;
if (id.startsWith("minecraft:")) {
return id.substring(10, id.length);
}
return id;
}
getComponent(id) {
if (this.behaviorPackBlockTypeDef === undefined) {
return undefined;
}
if (!Utilities_1.default.isUsableAsObjectKey(id)) {
Log_1.default.unsupportedToken(id);
throw new Error();
}
if (!this._managed[id]) {
const comp = this.behaviorPackBlockTypeDef.components[id];
if (comp) {
this._managed[id] = new ManagedComponent_1.ManagedComponent(this.behaviorPackBlockTypeDef.components, id, comp);
}
}
return this._managed[id];
}
notifyComponentUpdated(id) {
const component = this.getComponent(id);
if (component === undefined) {
Log_1.default.unexpectedUndefined("BTNCU");
}
else {
this._onComponentChanged.dispatch(this, component);
}
}
getAllComponents() {
return this.getComponents();
}
getComponents() {
const componentSet = [];
if (this.behaviorPackBlockTypeDef !== undefined) {
for (const componentName in this.behaviorPackBlockTypeDef.components) {
const component = this.getComponent(componentName);
if (component !== undefined) {
componentSet.push(component);
}
}
}
return componentSet;
}
ensurePropertyDefinition(propertyName, propertyDef) {
if (!this._properties[propertyName]) {
this._properties[propertyName] = propertyDef;
}
}
addComponent(id, componentOrData) {
this._ensureBehaviorPackDataInitialized();
const bpData = this.behaviorPackBlockTypeDef;
const mc = componentOrData instanceof ManagedComponent_1.ManagedComponent
? componentOrData
: new ManagedComponent_1.ManagedComponent(bpData.components, id, componentOrData);
bpData.components[id] = mc.getData();
this._managed[id] = mc;
this._onComponentAdded.dispatch(this, mc);
return mc;
}
removeComponent(id) {
if (this.behaviorPackBlockTypeDef === undefined) {
return;
}
const newBehaviorPacks = {};
const newComponents = {};
for (const name in this.behaviorPackBlockTypeDef.components) {
if (name !== id) {
const component = this.behaviorPackBlockTypeDef.components[name];
newBehaviorPacks[name] = component;
}
}
for (const name in this._managed) {
if (name !== id) {
newComponents[name] = this._managed[name];
}
}
this.behaviorPackBlockTypeDef.components = newBehaviorPacks;
this._managed = newComponents;
}
_ensureBehaviorPackDataInitialized() {
if (this.behaviorPackBlockTypeDef === undefined) {
this.behaviorPackBlockTypeDef = {
description: {
identifier: "unknown",
},
components: {},
events: {},
};
}
}
persist() {
if (this._behaviorPackFile === undefined) {
return false;
}
Log_1.default.assert(this._behaviorPackData !== null, "BTP");
if (!this._behaviorPackData) {
return false;
}
return this._behaviorPackFile.setObjectContentIfSemanticallyDifferent(this._behaviorPackData);
}
/**
* Loads the definition from the file.
* @param preserveComments If true, uses comment-preserving JSON parsing for edit/save cycles.
* If false (default), uses efficient standard JSON parsing.
* Can be called again with true to "upgrade" a read-only load to read/write.
*/
async load(preserveComments = false) {
// If already loaded with comments, we have the "best" version - nothing more to do
if (this._isLoaded && this._loadedWithComments) {
return;
}
// If already loaded without comments and caller doesn't need comments, we're done
if (this._isLoaded && !preserveComments) {
return;
}
if (this._behaviorPackFile === undefined) {
return;
}
if (!this._behaviorPackFile.isContentLoaded) {
await this._behaviorPackFile.loadContent();
}
if (!this._behaviorPackFile.content || this._behaviorPackFile.content instanceof Uint8Array) {
this._isLoaded = true;
this._loadedWithComments = preserveComments;
this._onLoaded.dispatch(this, this);
return;
}
let data = {};
// Use comment-preserving parser only when needed for editing
let result = preserveComments
? StorageUtilities_1.default.getJsonObjectWithComments(this._behaviorPackFile)
: StorageUtilities_1.default.getJsonObject(this._behaviorPackFile);
if (result) {
data = result;
}
this._behaviorPackData = data;
const block = data["minecraft:block"];
if (block.description) {
this.id = block.description.identifier;
}
this.behaviorPackBlockTypeDef = block;
this._isLoaded = true;
this._loadedWithComments = preserveComments;
this._onLoaded.dispatch(this, this);
}
}
exports.default = BlockType;