yoni-mcscripts-lib
Version:
为 Minecraft Script API 中的部分接口创建了 wrapper,并提供简单的事件管理器和任务管理器,另附有一些便于代码编写的一些小工具。
325 lines (324 loc) • 10.5 kB
JavaScript
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);
}
}