diff --git a/api/src/main/java/org/allaymc/api/block/component/BlockBaseComponent.java b/api/src/main/java/org/allaymc/api/block/component/BlockBaseComponent.java index b98d8913c..88f0940fb 100644 --- a/api/src/main/java/org/allaymc/api/block/component/BlockBaseComponent.java +++ b/api/src/main/java/org/allaymc/api/block/component/BlockBaseComponent.java @@ -1,6 +1,5 @@ package org.allaymc.api.block.component; -import com.google.common.base.Preconditions; import org.allaymc.api.block.BlockBehavior; import org.allaymc.api.block.data.BlockFace; import org.allaymc.api.block.dto.BlockStateWithPos; @@ -165,7 +164,7 @@ default double calculateBreakTime(BlockState blockState, ItemStack usedItem, Ent var efficiencyLevel = 0; if (entity != null) { - isInWater = entity.isInWater(); + isInWater = entity.isEyesInWater(); isOnGround = entity.isOnGround(); hasteEffectLevel = entity.getEffectLevel(EffectTypes.HASTE); // Conduit Power ensures at least level 2 haste effect diff --git a/api/src/main/java/org/allaymc/api/entity/component/EntityBaseComponent.java b/api/src/main/java/org/allaymc/api/entity/component/EntityBaseComponent.java index 54e2e3235..3d9ff303e 100644 --- a/api/src/main/java/org/allaymc/api/entity/component/EntityBaseComponent.java +++ b/api/src/main/java/org/allaymc/api/entity/component/EntityBaseComponent.java @@ -1,9 +1,8 @@ package org.allaymc.api.entity.component; import org.allaymc.api.block.data.BlockFace; +import org.allaymc.api.block.tag.BlockTags; import org.allaymc.api.block.type.BlockState; -import org.allaymc.api.block.type.BlockType; -import org.allaymc.api.block.type.BlockTypes; import org.allaymc.api.command.CommandSender; import org.allaymc.api.entity.Entity; import org.allaymc.api.entity.effect.EffectInstance; @@ -14,6 +13,7 @@ import org.allaymc.api.entity.type.EntityType; import org.allaymc.api.item.ItemStack; import org.allaymc.api.math.location.Location3fc; +import org.allaymc.api.utils.MathUtils; import org.allaymc.api.world.Dimension; import org.allaymc.api.world.World; import org.allaymc.api.world.chunk.Chunk; @@ -27,6 +27,7 @@ import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket; import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.UnmodifiableView; import org.joml.Vector3f; import org.joml.Vector3fc; @@ -48,243 +49,568 @@ public interface EntityBaseComponent extends EntityComponent, CommandSender, Has float DEFAULT_PUSH_SPEED_REDUCTION = 1f; float DEFAULT_KNOCKBACK = 0.4f; - private static boolean isWaterType(BlockType> blockType) { - return blockType == BlockTypes.FLOWING_WATER || blockType == BlockTypes.WATER; - } - + /** + * Gets the type of this entity. + * + * @return the type of this entity. + */ EntityType extends Entity> getEntityType(); + /** + * Gets the display name of this entity. + * + * @return the display name of this entity. + */ String getDisplayName(); + /** + * Sets the display name of this entity. + * + * @param displayName the display name to set. + */ void setDisplayName(String displayName); + /** + * Gets the name tag of this entity. + * + * @return the name tag of this entity. + */ default String getNameTag() { return getMetadata().get(EntityDataTypes.NAME); } + /** + * Sets the name tag of this entity. + * + * @param nameTag the name tag to set. + */ default void setNameTag(String nameTag) { setAndSendEntityData(EntityDataTypes.NAME, nameTag); } + /** + * Clears the name tag of this entity. + */ default void clearNameTag() { setAndSendEntityData(EntityDataTypes.NAME, ""); } + /** + * Gets the location of this entity. + * + * @return the location of this entity. + */ Location3fc getLocation(); /** * Set the location before the entity is spawned. *
* This method is usually used when you want to spawn the entity at a specific location. - *
* Then you need to set the entity's location before spawn the entity.
*
- * @param location The location you want to set
- *
+ * @param location the location you want to set
* @throws IllegalStateException if the entity is already spawned
*/
void setLocationBeforeSpawn(Location3fc location);
+ /**
+ * Gets the dimension of this entity.
+ *
+ * @return the dimension of this entity.
+ */
Dimension getDimension();
- World getWorld();
+ /**
+ * Gets the world of this entity.
+ *
+ * @return the world of this entity.
+ */
+ default World getWorld() {
+ return getLocation().dimension() != null ? getLocation().dimension().getWorld() : null;
+ }
+ /**
+ * Check if the entity will be spawned in the next tick.
+ *
+ * @return {@code true} if the entity will be spawned in the next tick.
+ */
boolean willBeSpawnedNextTick();
+ /**
+ * Check if the entity will be despawned in the next tick.
+ *
+ * @return {@code true} if the entity will be despawned in the next tick.
+ */
boolean willBeDespawnedNextTick();
+ /**
+ * Check if the entity is dead.
+ *
+ * @return {@code true} if the entity is dead.
+ */
boolean isDead();
+ /**
+ * Check if the entity is spawned.
+ *
+ * @return {@code true} if the entity is spawned.
+ */
boolean isSpawned();
+ /**
+ * Check if the entity is alive.
+ *
+ * @return {@code true} if the entity is alive.
+ */
default boolean isAlive() {
return isSpawned() && !isDead();
}
+ /**
+ * Check if the entity can be spawned.
+ *
+ * @return {@code true} if the entity can be spawned.
+ */
boolean canBeSpawned();
+ /**
+ * Teleport the entity to the specified location.
+ *
+ * @param location the location to teleport the entity to.
+ */
void teleport(Location3fc location);
+ /**
+ * Get the runtime id of this entity.
+ *
+ * @return the runtime id of this entity.
+ */
long getRuntimeId();
+ /**
+ * Same to {@link #getRuntimeId()}.
+ */
@Override
default long getLongId() {
return getRuntimeId();
}
+ /**
+ * Get the unique id of this entity.
+ *
+ * @return the unique id of this entity.
+ */
long getUniqueId();
+ /**
+ * Get the metadata of this entity.
+ *
+ * @return the metadata of this entity.
+ */
Metadata getMetadata();
+ /**
+ * Send the entity data to the viewers.
+ *
+ * @param dataTypes the data types to send.
+ */
void sendEntityData(EntityDataType>... dataTypes);
+ /**
+ * Send the entity flags to the viewers.
+ *
+ * @param flags the flags to send.
+ */
void sendEntityFlags(EntityFlag... flags);
+ /**
+ * Set and send the entity data to the viewers.
+ *
+ * @param dataType the data type to set.
+ * @param value the value to set.
+ * @param
+ * This method will only remove the entity from the viewers, but the entity will still exist in the world.
+ */
void despawnFromAll();
+ /**
+ * Despawn the entity.
+ *
+ * Compared to {@link #despawnFromAll()}, this method will also remove the entity from the world.
+ */
void despawn();
+ /**
+ * Create the spawn packet of this entity.
+ *
+ * @return the spawn packet of this entity.
+ */
BedrockPacket createSpawnPacket();
+ /**
+ * Send a packet to the viewers of this entity.
+ *
+ * @param packet the packet to send.
+ */
void sendPacketToViewers(BedrockPacket packet);
+ /**
+ * Send a packet to the viewers of this entity immediately.
+ *
+ * @param packet the packet to send.
+ */
void sendPacketToViewersImmediately(BedrockPacket packet);
- void broadcastMoveToViewers(Location3fc newLoc);
-
- void broadcastMoveToViewers(Location3fc newLoc, boolean teleporting);
-
+ /**
+ * Save the entity to NBT.
+ *
+ * @return the NBT of this entity.
+ */
NbtMap saveNBT();
+ /**
+ * Save the entity to NBT without position.
+ *
+ * @return the NBT of this entity without position.
+ */
default NbtMap saveNBTWithoutPos() {
var builder = saveNBT().toBuilder();
builder.remove("Pos");
return builder.build();
}
+ /**
+ * Load the entity from NBT.
+ *
+ * @param nbt the NBT to load.
+ */
void loadNBT(NbtMap nbt);
+ /**
+ * Get the fall distance of this entity.
+ *
+ * @return the fall distance of this entity.
+ */
float getFallDistance();
+ /**
+ * Called when the entity falls.
+ */
+ @ApiStatus.OverrideOnly
void onFall();
+ /**
+ * Check if the entity has the specified effect.
+ *
+ * @param effectType the effect type to check.
+ * @return {@code true} if the entity has the specified effect, otherwise {@code false}.
+ */
boolean hasEffect(EffectType effectType);
+ /**
+ * Get the effect level of the specified effect.
+ *
+ * @param effectType the effect type to get.
+ * @return the effect level of the specified effect.
+ */
int getEffectLevel(EffectType effectType);
+ /**
+ * Add the specified effect to the entity.
+ *
+ * @param effectInstance the effect instance to add.
+ */
void addEffect(EffectInstance effectInstance);
+ /**
+ * Remove the specified effect from the entity.
+ *
+ * @param effectType the effect type to remove.
+ */
void removeEffect(EffectType effectType);
+ /**
+ * Remove all effects from the entity.
+ */
void removeAllEffects();
+ /**
+ * Called when the entity ticks.
+ *
+ * @param currentTick the current tick.
+ */
+ @ApiStatus.OverrideOnly
default void tick(long currentTick) {}
+ /**
+ * Check if the entity has head yaw.
+ *
+ * @return {@code true} if the entity has head yaw, otherwise {@code false}.
+ */
default boolean enableHeadYaw() {
return false;
}
+ /**
+ * Get the base offset of this entity.
+ *
+ * @return the base offset of this entity.
+ */
default float getBaseOffset() {
return 0f;
}
- default AABBf getOffsetAABB() {
- return getAABB().translate(getLocation(), new AABBf());
- }
-
+ /**
+ * Check whether the entity's movement should be computed server-side.
+ *
+ * @return {@code true} if the entity's movement should be computed server-side, otherwise {@code false}.
+ */
default boolean computeMovementServerSide() {
return true;
}
+ /**
+ * Get the step height of this entity.
+ *
+ * @return the step height of this entity.
+ */
default float getStepHeight() {
return 0.6f;
}
+ /**
+ * Get the gravity of this entity.
+ *
+ * @return the gravity of this entity.
+ */
default float getGravity() {
return 0.08f;
}
- default float getEyeHeight() {
- return (getAABB().maxY() - getAABB().minY()) * 0.9f;
- }
-
+ /**
+ * Check if the entity has gravity.
+ *
+ * @return {@code true} if the entity has gravity, otherwise {@code false}.
+ */
default boolean hasGravity() {
return getMetadata().get(EntityFlag.HAS_GRAVITY);
}
- void setHasGravity(boolean hasGravity);
+ /**
+ * Set if the entity has gravity.
+ *
+ * @param hasGravity {@code true} if the entity has gravity, otherwise {@code false}.
+ */
+ default void setHasGravity(boolean hasGravity) {
+ setAndSendEntityFlag(EntityFlag.HAS_GRAVITY, hasGravity);
+ }
+
+ /**
+ * Get the eye height of this entity.
+ *
+ * @return the eye height of this entity.
+ */
+ default float getEyeHeight() {
+ return (getAABB().maxY() - getAABB().minY()) * 0.9f;
+ }
/**
- * Given yaw, if the movement multiplier is not 0, the entity will move towards the direction specified by yaw.
+ * Get the movement factor of this entity.
+ *
+ * Given yaw, if the movement multiplier is not 0, the entity will move towards the direction specified by yaw.
*
+ * @return the movement factor of this entity.
* @see Horizontal Movement Formulas
*/
default float getMovementFactor() {
return STOP_MOVEMENT_FACTOR;
}
+ /**
+ * Get the push speed reduction of this entity.
+ *
+ * @return the push speed reduction of this entity.
+ */
default float getPushSpeedReduction() {
return DEFAULT_PUSH_SPEED_REDUCTION;
}
+ /**
+ * Check if the chunk which the entity's location is in is loaded.
+ *
+ * @return {@code true} if the chunk is loaded, otherwise {@code false}.
+ */
default boolean isCurrentChunkLoaded() {
var loc = getLocation();
var cx = (int) loc.x() >> 4;
@@ -292,15 +618,30 @@ default boolean isCurrentChunkLoaded() {
return loc.dimension().getChunkService().isChunkLoaded(cx, cz);
}
+ /**
+ * Check if the y coordinate of the entity's location is in the range of the dimension.
+ *
+ * @return {@code true} if the y coordinate is in the range, otherwise {@code false}.
+ */
default boolean isYInRange() {
var loc = getLocation();
return loc.dimension().isYInRange(loc.y());
}
+ /**
+ * Check if the entity is in the world.
+ *
+ * @return {@code true} if the entity is in the world, otherwise {@code false}.
+ */
default boolean isInWorld() {
return isYInRange() && isCurrentChunkLoaded();
}
+ /**
+ * Get the current chunk of the entity.
+ *
+ * @return the current chunk of the entity.
+ */
default Chunk getCurrentChunk() {
var loc = getLocation();
var cx = (int) loc.x() >> 4;
@@ -308,6 +649,11 @@ default Chunk getCurrentChunk() {
return loc.dimension().getChunkService().getChunk(cx, cz);
}
+ /**
+ * Get the horizontal face of the entity.
+ *
+ * @return the horizontal face of the entity.
+ */
default BlockFace getHorizontalFace() {
var rotation = getLocation().yaw() % 360;
if (rotation < 0) rotation += 360.0;
@@ -323,16 +669,40 @@ default BlockFace getHorizontalFace() {
}
}
+ /**
+ * Knockback the entity.
+ *
+ * @param source the source of the knockback.
+ */
default void knockback(Vector3fc source) {
knockback(source, DEFAULT_KNOCKBACK);
}
+ /**
+ * Knockback the entity.
+ *
+ * @param source the source of the knockback.
+ * @param kb the knockback strength to apply.
+ */
default void knockback(Vector3fc source, float kb) {
knockback(source, kb, false);
}
+ /**
+ * Knockback the entity.
+ *
+ * @param source the source of the knockback.
+ * @param kb the knockback strength to apply.
+ * @param ignoreKnockbackResistance {@code true} if the knockback resistance should be ignored.
+ */
void knockback(Vector3fc source, float kb, boolean ignoreKnockbackResistance);
+ /**
+ * Apply the entity event to the entity.
+ *
+ * @param event the entity event to apply.
+ * @param data the data of the entity event.
+ */
default void applyEntityEvent(EntityEventType event, int data) {
var pk = new EntityEventPacket();
pk.setRuntimeEntityId(getRuntimeId());
@@ -341,11 +711,22 @@ default void applyEntityEvent(EntityEventType event, int data) {
sendPacketToViewers(pk);
}
- default void applyAnimation(AnimatePacket.Action action) {
- applyAnimation(action, 0);
+ /**
+ * Apply an action to the entity.
+ *
+ * @param action the action to apply.
+ */
+ default void applyAction(AnimatePacket.Action action) {
+ applyAction(action, 0);
}
- default void applyAnimation(AnimatePacket.Action action, float rowingTime) {
+ /**
+ * Apply an action to the entity.
+ *
+ * @param action the action of the action.
+ * @param rowingTime the rowing time of the action.
+ */
+ default void applyAction(AnimatePacket.Action action, float rowingTime) {
var pk = new AnimatePacket();
pk.setRuntimeEntityId(getRuntimeId());
pk.setAction(action);
@@ -353,37 +734,81 @@ default void applyAnimation(AnimatePacket.Action action, float rowingTime) {
sendPacketToViewers(pk);
}
+ /**
+ * Check if the entity can critical attack.
+ *
+ * @return {@code true} if the entity can critical attack, otherwise {@code false}.
+ */
default boolean canCriticalAttack() {
return !isOnGround() && getMotion().y() < 0 && !hasEffect(EffectTypes.BLINDNESS) && !hasEffect(EffectTypes.SLOW_FALLING);
}
+ /**
+ * Add a tag to the entity.
+ *
+ * @param tag the tag to add.
+ * @return {@code true} if the tag is added, otherwise {@code false}.
+ */
boolean addTag(String tag);
+ /**
+ * Remove a tag from the entity.
+ *
+ * @param tag the tag to remove.
+ * @return {@code true} if the tag is removed, otherwise {@code false}.
+ */
boolean removeTag(String tag);
+ /**
+ * Check if the entity has the specified tag.
+ *
+ * @param tag the tag to check.
+ * @return {@code true} if the entity has the specified tag, otherwise {@code false}.
+ */
boolean hasTag(String tag);
+ /**
+ * Get the tags of the entity.
+ *
+ * @return the tags of the entity.
+ */
@UnmodifiableView
Set