@occultus/entity-api
Version:
Star Tenon entity api and utils
269 lines (252 loc) • 7.77 kB
text/typescript
import {
Container,
Effect,
EffectType,
Entity,
EntityEffectOptions,
EntityEquippableComponent,
EquipmentSlot,
ItemStack,
Player,
} from "@minecraft/server";
import { EffectData, effectGroupMap, EffectGroups } from "@occultus/common";
import { OccultusSDKError } from "@occultus/core";
/**
* 尝试对实体进行操作
*
* @param entity 要操作的实体
* @param operate 操作函数,接受一个实体作为参数
* @returns 操作是否成功
* @since Starock 0.6.0 (0.1.0)
*/
export function tryOperateEntity(
entity: Entity,
operate: (entity: Entity) => void
): boolean {
if (entity.isValid) {
operate(entity);
return true;
}
return false;
}
/**
* 获取给定实体的指定槽位物品
*
* **IMPORTANT: 受原版接口限制, 当前只能获取玩家的装备**
* @param entity 要获取槽位的实体
* @param slot 要获取的槽位,默认为 {@link EquipmentSlot.Mainhand}
* @return 槽位中的物品
* @since Starock 0.6.0 (0.1.0)
*/
export function getEquipmentItem(
entity: Entity,
slot = EquipmentSlot.Mainhand
): ItemStack | undefined {
const equipment = entity?.getComponent(
"minecraft:equippable"
) as EntityEquippableComponent;
return equipment?.getEquipment(slot);
}
/**
* 设置给定实体的指定槽位物品
*
* **IMPORTANT: 受原版接口限制, 当前只能获取玩家的装备**
* @param entity 要设置槽位的实体
* @param item 要设置的物品,如果为undefined则清空该槽位
* @param slot 要设置的槽位,默认为 {@link EquipmentSlot.Mainhand}
* @since Starock 0.6.0 (0.1.0)
*/
export function setEquipmentItem(
entity: Entity,
item?: ItemStack,
slot: EquipmentSlot = EquipmentSlot.Mainhand
): boolean {
const equipment = entity?.getComponent(
"minecraft:equippable"
) as EntityEquippableComponent;
if(!equipment) return false;
return equipment?.setEquipment(slot, item);
}
/**
* 获取实体的容器
*
* @param entity 要获取容器的实体
* @return 实体的容器,如果没有则返回`undefined`
* @since Starock 0.6.0 (0.1.0)
*/
export function getContainer(entity: Entity): Container | undefined {
return entity.getComponent("minecraft:inventory")?.container;
}
/**
* 设置实体的槽位物品
* @param entity 要设置槽位物品的实体
* @param slot 槽位索引,从0开始依次递增
* @param item 要设置的物品
* @since Starock 0.6.0 (0.1.0)
*/
export function setSlot(entity: Entity, slot: number, item?: ItemStack): void {
getContainer(entity)?.setItem(slot, item);
}
/**
* 给予实体物品
* @param entity 要给予物品的实体
* @param item 要给予的物品
* @since Starock 0.6.0 (0.1.0)
*/
export function giveItem(entity: Entity, item: ItemStack): void {
const container = getContainer(entity);
if (container && container.emptySlotsCount > 0) {
container.addItem(item);
} else {
entity.dimension.spawnItem(item, entity.location);
}
}
/**
* 清空实体的容器
* @param entity 要清空容器的实体
* @since Starock 0.6.0 (0.1.0)
*/
export function clearSlot(entity: Entity): void {
getContainer(entity)?.clearAll();
}
/**
* 将 {@link EffectData} 应用到实体上
* @param entity 要应用 {@link EffectData} 的实体
* @param data 要应用的 {@link EffectData}
* @returns 应用后的状态效果
* @since Starock 0.6.0 (0.1.0)
*/
export function applyEffectData(
entity: Entity,
data: EffectData | EffectData[]
): (Effect | undefined)[] {
if (!Array.isArray(data)) {
data = [data];
}
return data.flatMap((effectData) => {
return entity.addEffect(effectData.effectType, effectData.duration, {
amplifier: effectData.amplifier,
showParticles: effectData.showParticles,
});
});
}
/**
* 向实体移除状态效果
* @param entity 要清除效果的实体.
* @param effectType 状态效果类型,可以是单个效果类型、效果类型数组、字符串、字符串数组或{@link EffectGroups}枚举值
* @since Starock 0.6.0 (0.1.0)
*/
export function clearEffect(
entity: Entity,
effectType: EffectType | EffectType[] | string | string[] | EffectGroups
): void {
const effects =
effectGroupMap[effectType as EffectGroups] ||
(Array.isArray(effectType) ? effectType : [effectType]);
for (const effect of effects) {
entity.removeEffect(effect);
}
}
/**
* 向实体添加状态效果
* @param entity 要添加状态效果实体对象
* @param effectType 状态效果类型,可以是单个效果类型、效果类型数组、字符串、字符串数组或{@link EffectGroups}枚举值
* @param duration
* 状态效果持续时间,以刻为单位 *(20刻 = 1秒)*
*
* 其值必须在范围`[0, 20000000]`内
* @param options 状态效果选项
* @since Starock 0.6.0 (0.1.0)
*/
export function addEffect(
entity: Entity,
effectType: EffectType | EffectType[] | string | string[] | EffectGroups,
duration: number,
options?: EntityEffectOptions
): void {
const effects =
effectGroupMap[effectType as EffectGroups] ||
(Array.isArray(effectType) ? effectType : [effectType]);
for (const effect of effects) {
entity.addEffect(effect, duration, options);
}
}
/**
* 获取实体所有的族
* @param entity 要获取族的实体
* @return 实体的族数组,如果没有则返回`undefined`
* @see https://zh.minecraft.wiki/w/族
* @see https://minecraft.wiki/w/Family
*/
export function getFamilies(entity: Entity): string[] | undefined {
return entity.getComponent("minecraft:type_family")?.getTypeFamilies();
}
/**
* 返回实体是否含有指定族
* @param entity 要检查族的实体
* @return 实体是否含有指定族
* @see https://zh.minecraft.wiki/w/族
* @see https://minecraft.wiki/w/Family
*/
export function hasFamily(entity: Entity, family: string): boolean {
return (
entity.getComponent("minecraft:type_family")?.hasTypeFamily(family) ?? false
);
}
/**
* 根据玩家等级计算经验消耗
* @param level 玩家等级
*/
export function getExpCost(level: number): number {
if (level >= 30) {
return 62 + (level - 30) * 7;
}
if (level >= 15) {
return 17 + (level - 15) * 3;
}
return 17;
}
/**
* 获取玩家得到的所有经验值
* @param player
*/
export function getAllExp(player: Player): number {
const level: number = player.level;
let exp = 0;
for (let i = 1; i <= level; i++) {
exp += getExpCost(i);
}
return exp + player.xpEarnedAtCurrentLevel;
}
function consumeAmount(item: ItemStack, value: number): ItemStack | undefined {
const amount: number = item.amount;
if (amount === value) {
return undefined;
}
if (amount - value < 0) {
throw new OccultusSDKError(
`The number of items is insufficient! Actual amount: ${amount} Consume amount: ${value}`,
`${item.typeId}`
);
}
if (amount - value > item.maxAmount) {
throw new OccultusSDKError(
`The max stack of items is insufficient!`,
`${item.typeId}`
);
}
const newItem: ItemStack = item.clone();
newItem.amount = amount - value;
return newItem;
}
/**
* 消耗玩家装备物品的数量
* @param player 要消耗物品的玩家
* @param amount 消耗的数量
* @returns 是否消耗成功
*/
export function consumeEquipmentAmount(player: Player, amount: number = 1): boolean {
const item = getEquipmentItem(player);
if (!item) return false;
return setEquipmentItem(player, consumeAmount(item, amount));
}