UNPKG

yoni-mcscripts-lib

Version:

为 Minecraft Script API 中的部分接口创建了 wrapper,并提供简单的事件管理器和任务管理器,另附有一些便于代码编写的一些小工具。

325 lines (324 loc) 10.5 kB
import { EntityWraps } from "./remix/entity/EntityWraps.js"; import { VanillaWorld } from "./basis.js"; import { getAllVanillaDimensions } from "./dimensionutils.js"; /** * 一系列处理实体的方法。 */ export class EntityUtils { /** * 检查一个对象是否为实体对象。 * @param {any} object - 任意。 * @throws 当不是实体的时候抛出错误。 */ static checkIsEntity(object) { if (!EntityUtils.isEntity(object)) { throw new TypeError("Not a Entity type"); } } /** * (内部方法)由实体对象创建对应的 YoniEntity 实体对象。 * * 一般情况下你不应该使用此方法,而是使用 {@link EntityUtils.getYoniEntity}。 * @param {any} entity - 可以被认为是实体的东西。 * @return {YoniEntity} 如果 `entity` 不为实体类型,则返回 `null`。 */ static from(entity) { if (EntityUtils.isYoniEntity(entity)) return entity; return EntityWraps.fromSourceEntity(entity); } /** * 检测指定实体对象是否为玩家实体对象。 * @param {EntityValue} entity 要检测的实体。 * @returns {boolean} */ static entityIsPlayer(entity) { return EntityUtils.isPlayer(EntityUtils.getMinecraftEntity(entity)); } /** * 获取实体的已经存在于世界上的 YoniEntity 对象实例。 */ static getAliveEntity(entity) { return EntityUtils.from(EntityUtils.getAliveVanillaEntity(entity)); } /** * 获取实体的已经存在于世界上的香草实体对象实例。 */ static getAliveVanillaEntity(entity) { if (entity.isValid()) return EntityUtils.getMinecraftEntity(entity); const result = VanillaWorld.getEntity(entity.id); if (result) { return result; } throw new ReferenceError("no entity found"); } /** * 获取所有已经载入的实体的对象实例。 * @param {Minecraft.EntityQueryOptions} option * @return {YoniEntity[]} */ static getAliveEntities(option) { return Array.from(EntityUtils.getDimensionVanillaEntities()).map(EntityUtils.from); } /** * 获取活体实体的 `minecraft:health` 组件。 * @param {EntityValue} entity * @returns {Minecraft.EntityHealthComponent} */ static getHealthComponent(entity) { EntityUtils.checkIsEntity(entity); const comp = entity.getComponent("minecraft:health"); if (comp == undefined) throw new Error("not a living entity"); return comp; } /** * 尝试获取实体的 `minecraft:health` 组件。 * @param {EntityValue} entity * @returns {Minecraft.EntityHealthComponent} */ static tryGetHealthComponent(entity) { EntityUtils.checkIsEntity(entity); return entity.getComponent("minecraft:health") ?? false; } /** * 获取实体的物品栏容器对象。 * @param {EntityValue} entity * @returns {Minecraft.InventoryComponentContainer} */ static getInventory(entity) { EntityUtils.checkIsEntity(entity); if (EntityUtils.#inventoryCache.has(entity)) return EntityUtils.#inventoryCache.get(entity); const comp = entity.getComponent("minecraft:inventory"); if (comp == undefined) throw new ReferenceError("no inventory container in the entity"); const inv = comp.container; EntityUtils.#inventoryCache.set(entity, inv); return inv; } static #inventoryCache = new WeakMap(); /** * 获取玩家主手上的物品。 */ static getItemInMainHand(entity) { return EntityUtils.getInventory(entity).getItem(entity.selectedSlot); } /** * 设置玩家主手上的物品。 */ static setItemInMainHand(entity, item) { EntityUtils.getInventory(entity).setItem(entity.selectedSlot, item); } /** * 获取活体实体的血量。 * @param {EntityValue} entity * @returns {number} */ static getCurrentHealth(entity) { const component = EntityUtils.tryGetHealthComponent(entity); return component ? component.currentValue : 0; } /** * 遍历所有维度以获取所有存活着的实体。 */ static getDimensionVanillaEntities(options) { const dimensionArrays = getAllVanillaDimensions(); let entitiesArrays; if (!options) { entitiesArrays = dimensionArrays.map(dim => dim.getEntities()); } else { entitiesArrays = dimensionArrays.map(dim => dim.getEntities(options)); } return [].concat(...entitiesArrays); } /** * 获取世界内存在的玩家。 */ static getWorldVanillaPlayers(options) { return VanillaWorld.getPlayers(options); } /** * 获取世界内存在的所有实体(这包括死亡的玩家)。 * * 尽管此方法已经尽了它最大的努力,但是对于一些特殊实体(如minecraft:agent),它们仍然不包含在返回结果中。 * @returns {EntityValue[]} */ static getLoadedVanillaEntities() { return [].concat(EntityUtils.getDimensionVanillaEntities({ excludeTypes: ["minecraft:player"] }), EntityUtils.getWorldVanillaPlayers()); } /** * 获取实体可达到的最大血量。 * @param {EntityValue} entity * @returns {number} */ static getMaxHealth(entity) { const component = EntityUtils.tryGetHealthComponent(entity); return component ? component.effectiveMax : 0; } /** * 获取实体对象对应的 Minecraft.Entity。 * @param {EntityValue} entity * @returns {MinecraftEntityType} */ static getMinecraftEntity(entity) { if (EntityUtils.isMinecraftEntity(entity)) return entity; else if (EntityUtils.isYoniEntity(entity)) return entity.vanillaEntity; throw new Error("no reference or not an entity"); } /** * 获取实体对象对应的 YoniEntity。 * @param {EntityValue} entity * @returns {YoniEntityType} * @throws 如果参数不是实体将会抛出错误 */ static getYoniEntity(entity) { EntityUtils.checkIsEntity(entity); return EntityUtils.from(entity); } /** * 检测实体是否有所有指定的家族。 * @param {EntityValue} entity * @param {...string} families * @returns {boolean} */ static hasFamilies(entity, ...families) { entity = EntityUtils.getMinecraftEntity(entity); const dimension = entity.dimension; const tryEntities = dimension.getEntities({ type: entity.typeId, families: families }); for (const cEntity of tryEntities) { if (entity === cEntity) { return true; } } return false; } /** * 检测实体是否有任一指定的家族。 * @param {EntityValue} entity * @param {...string} families * @returns {boolean} */ static hasAnyFamily(entity, ...families) { entity = EntityUtils.getMinecraftEntity(entity); const dimension = entity.dimension; for (const family of families) { const tryEntities = dimension.getEntities({ type: entity.typeId, families: Array.of(family) }); for (const cEntity of tryEntities) { if (entity === cEntity) { return true; } } } return false; } /** * 检测实体是否含有指定家族。 * @param {EntityValue} entity * @param {string} family * @returns {boolean} */ static hasFamily(entity, family) { return EntityUtils.hasFamilies(entity, family); } /** * 检测一个实体是否存在于世界上。 * @param {EntityValue} entity * @returns {boolean} */ static isAliveEntity(entity) { EntityUtils.checkIsEntity(entity); return entity.isValid(); } /** * 检测一个实体是否活着。 * * 例如;物品、箭、烟花不是生物实体。 * @param {EntityValue} entity * @returns {boolean} */ static isLivingEntity(entity) { EntityUtils.checkIsEntity(entity); const comp = entity.getComponent("minecraft:health"); if (comp == undefined) return false; return comp.currentValue > 0; } /** * 检测参数是否为实体。 * @param {any} obj * @returns {boolean} */ static isEntity(object) { if (EntityUtils.isYoniEntity(object)) return true; if (EntityUtils.isMinecraftEntity(object)) return true; return false; } /** * 检测参数是否为玩家实体。 * @param {any} obj * @returns {boolean} */ static isPlayer(object) { return EntityUtils.isEntity(object) && object.typeId === "minecraft:player"; } /** * 检测参数是否为原版实体。 * @param {any} object * @returns {boolean} */ static isMinecraftEntity(object) { if (object != null) try { return EntityWraps.prototypeWraps.has(Object.getPrototypeOf(object)); } catch { // no thing } return false; } /** * 检测两个参数是否为实体且代表同一实体 */ static isSameEntity(entity1, entity2) { return EntityUtils.isEntity(entity1) && EntityUtils.isEntity(entity2) && entity1.id === entity2.id; } /** * 检测参数是否为 YoniEntity。 * @param {any} object * @returns {boolean} */ static isYoniEntity(object) { if (object != null) try { return EntityWraps.srcPrototypeWraps.has(Object.getPrototypeOf(object)); } catch { //no thing } return false; } /** * 设置实体的血量。 * @param {EntityValue} entity * @param {number} val */ static setCurrentHealth(entity, value) { EntityUtils.getHealthComponent(entity) .setCurrentValue(value); } }