introduce tickables and ai boilerplate
This commit is contained in:
parent
e52bc7474a
commit
41da2e4c8e
15 changed files with 352 additions and 9 deletions
|
@ -5,8 +5,21 @@
|
|||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="9d5d9b6f-43c8-41a4-bb42-a66ffc96c9b0" name="Changes" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/src/main/java/me/tofaa/entitylib/Tickable.java" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/main/java/me/tofaa/entitylib/TickingContainer.java" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/main/java/me/tofaa/entitylib/entity/WrapperEntityCreature.java" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/main/java/me/tofaa/entitylib/entity/WrapperExperienceOrbEntity.java" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/main/java/me/tofaa/entitylib/entity/ai/AIGroup.java" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/main/java/me/tofaa/entitylib/entity/ai/GoalSelector.java" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/main/java/me/tofaa/entitylib/entity/ai/TargetSelector.java" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/main/java/me/tofaa/entitylib/world/WrapperWorld.java" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/test-plugin/src/main/java/me/tofaa/entitylib/SpawnClickableFrogCommand.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/test-plugin/src/main/java/me/tofaa/entitylib/TestDisplayCommand.java" beforeDir="false" afterPath="$PROJECT_DIR$/test-plugin/src/main/java/me/tofaa/entitylib/TestDisplayCommand.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/me/tofaa/entitylib/EntityLib.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/me/tofaa/entitylib/EntityLib.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/me/tofaa/entitylib/entity/WrapperEntity.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/me/tofaa/entitylib/entity/WrapperEntity.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/me/tofaa/entitylib/entity/WrapperLivingEntity.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/me/tofaa/entitylib/entity/WrapperLivingEntity.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/test-plugin/src/main/java/me/tofaa/entitylib/EntityLibPlugin.java" beforeDir="false" afterPath="$PROJECT_DIR$/test-plugin/src/main/java/me/tofaa/entitylib/EntityLibPlugin.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/test-plugin/src/main/resources/plugin.yml" beforeDir="false" afterPath="$PROJECT_DIR$/test-plugin/src/main/resources/plugin.yml" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
|
@ -77,7 +90,7 @@
|
|||
"WebServerToolWindowFactoryState": "false",
|
||||
"git-widget-placeholder": "master",
|
||||
"ignore.virus.scanning.warn.message": "true",
|
||||
"jdk.selected.JAVA_MODULE": "corretto-1.8",
|
||||
"jdk.selected.JAVA_MODULE": "corretto-17",
|
||||
"kotlin-language-version-configured": "true",
|
||||
"last_opened_file_path": "D:/Github/EntityLib",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
|
@ -208,7 +221,7 @@
|
|||
<workItem from="1704485939274" duration="2440000" />
|
||||
<workItem from="1704502790346" duration="6191000" />
|
||||
<workItem from="1705192736239" duration="496000" />
|
||||
<workItem from="1705534524814" duration="1694000" />
|
||||
<workItem from="1705534524814" duration="8652000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
|
|
|
@ -5,15 +5,13 @@ import com.github.retrooper.packetevents.event.PacketListenerAbstract;
|
|||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import me.tofaa.entitylib.entity.EntityIdProvider;
|
||||
import me.tofaa.entitylib.entity.EntityInteractionProcessor;
|
||||
import me.tofaa.entitylib.entity.WrapperEntity;
|
||||
import me.tofaa.entitylib.entity.WrapperLivingEntity;
|
||||
import me.tofaa.entitylib.entity.*;
|
||||
import me.tofaa.entitylib.exception.InvalidVersionException;
|
||||
import me.tofaa.entitylib.meta.EntityMeta;
|
||||
import me.tofaa.entitylib.meta.Metadata;
|
||||
|
@ -116,6 +114,28 @@ public final class EntityLib {
|
|||
return entities.get(uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a custom entity to EntityLib. This exists to allow developers to extend {@link WrapperEntity} and its subclasses simply and define their own logic.
|
||||
* <p>
|
||||
* This method DOES NOT create a new entity, it simply registers the entity to EntityLib.
|
||||
* To construct a {@link WrapperEntity} you need to call {@link EntityLib#createMeta(int, EntityType)} and pass the created metadata to the constructor of the entity.
|
||||
* <br>
|
||||
* This method will throw a RuntimeException if an entity with the given uuid or id already exists.
|
||||
* <br>
|
||||
* The entity is not modified in any way, simply stored internally for it to be accessible thru {@link EntityLib#getEntity(UUID)} and {@link EntityLib#getEntity(int)}.
|
||||
* </p>
|
||||
* @param entity the entity to register
|
||||
* @return the same entity passed.
|
||||
* @param <T> instance of WrapperEntity, used to infer its type.
|
||||
*/
|
||||
public static @NotNull <T extends WrapperEntity> T register(@NotNull T entity) {
|
||||
checkInit();
|
||||
if (entities.containsKey(entity.getUuid())) throw new RuntimeException("An entity with that uuid already exists");
|
||||
if (entitiesById.containsKey(entity.getEntityId())) throw new RuntimeException("An entity with that id already exists");
|
||||
entities.put(entity.getUuid(), entity);
|
||||
entitiesById.put(entity.getEntityId(), entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WrapperEntity with the given UUID and EntityType.
|
||||
|
@ -136,6 +156,9 @@ public final class EntityLib {
|
|||
if (meta instanceof LivingEntityMeta) {
|
||||
entity = new WrapperLivingEntity(entityId, uuid, entityType, meta);
|
||||
}
|
||||
else if (entityType == EntityTypes.EXPERIENCE_ORB) {
|
||||
entity = new WrapperExperienceOrbEntity(entityId, uuid, entityType, meta);
|
||||
}
|
||||
else {
|
||||
entity = new WrapperEntity(entityId, uuid, entityType, meta);
|
||||
}
|
||||
|
@ -148,6 +171,24 @@ public final class EntityLib {
|
|||
return createEntity(entityIdProvider.provide(), uuid, entityType);
|
||||
}
|
||||
|
||||
public static @NotNull WrapperEntityCreature createEntityCreature(int entityId, @NotNull UUID uuid, @NotNull EntityType entityType) {
|
||||
checkInit();
|
||||
if (entities.containsKey(uuid)) throw new RuntimeException("An entity with that uuid already exists");
|
||||
if (entitiesById.containsKey(entityId)) throw new RuntimeException("An entity with that id already exists");
|
||||
EntityMeta meta = createMeta(entityId, entityType);
|
||||
if (!(meta instanceof LivingEntityMeta)) {
|
||||
throw new RuntimeException("Entity type " + entityType + " is not a living entity, EntityCreature requires a living entity");
|
||||
}
|
||||
WrapperEntityCreature entity = new WrapperEntityCreature(entityId, uuid, entityType, meta);
|
||||
entities.put(uuid, entity);
|
||||
entitiesById.put(entityId, entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static @NotNull WrapperEntityCreature createEntityCreature(@NotNull UUID uuid, @NotNull EntityType entityType) {
|
||||
return createEntityCreature(entityIdProvider.provide(), uuid, entityType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param entityId the entity id
|
||||
* @return the metadata of the entity with the given id. If the entity does not exist, this method will return null.
|
||||
|
|
7
src/main/java/me/tofaa/entitylib/Tickable.java
Normal file
7
src/main/java/me/tofaa/entitylib/Tickable.java
Normal file
|
@ -0,0 +1,7 @@
|
|||
package me.tofaa.entitylib;
|
||||
|
||||
public interface Tickable {
|
||||
|
||||
void update(long time);
|
||||
|
||||
}
|
17
src/main/java/me/tofaa/entitylib/TickingContainer.java
Normal file
17
src/main/java/me/tofaa/entitylib/TickingContainer.java
Normal file
|
@ -0,0 +1,17 @@
|
|||
package me.tofaa.entitylib;
|
||||
|
||||
public interface TickingContainer {
|
||||
|
||||
void addTickable(Tickable tickable);
|
||||
|
||||
void removeTickable(Tickable tickable);
|
||||
|
||||
default void update(long time) {
|
||||
for (Tickable tickable : getTickables()) {
|
||||
tickable.update(time);
|
||||
}
|
||||
}
|
||||
|
||||
Iterable<Tickable> getTickables();
|
||||
|
||||
}
|
|
@ -4,17 +4,18 @@ import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
|
|||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import com.github.retrooper.packetevents.protocol.world.Location;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3f;
|
||||
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.*;
|
||||
import me.tofaa.entitylib.EntityLib;
|
||||
import me.tofaa.entitylib.Tickable;
|
||||
import me.tofaa.entitylib.meta.EntityMeta;
|
||||
import me.tofaa.entitylib.meta.types.ObjectData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
|
||||
public class WrapperEntity {
|
||||
public class WrapperEntity implements Tickable {
|
||||
private final EntityType entityType;
|
||||
private final int entityId;
|
||||
private final Optional<UUID> uuid;
|
||||
|
@ -78,6 +79,15 @@ public class WrapperEntity {
|
|||
return true;
|
||||
}
|
||||
|
||||
public boolean hasNoGravity() {
|
||||
return meta.isHasNoGravity();
|
||||
}
|
||||
|
||||
public void setHasNoGravity(boolean hasNoGravity) {
|
||||
meta.setHasNoGravity(hasNoGravity);
|
||||
refresh();
|
||||
}
|
||||
|
||||
public void rotateHead(float yaw, float pitch) {
|
||||
sendPacketToViewers(
|
||||
new WrapperPlayServerEntityRotation(entityId, yaw, pitch, onGround)
|
||||
|
@ -88,6 +98,14 @@ public class WrapperEntity {
|
|||
rotateHead(location.getYaw(), location.getPitch());
|
||||
}
|
||||
|
||||
public void rotateHead(WrapperEntity entity) {
|
||||
rotateHead(entity.getLocation());
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return new Location(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
if (!spawned) return;
|
||||
spawned = false;
|
||||
|
@ -176,12 +194,47 @@ public class WrapperEntity {
|
|||
return spawned;
|
||||
}
|
||||
|
||||
public boolean hasVelocity() {
|
||||
if (isOnGround()) {
|
||||
// if the entity is on the ground and only "moves" downwards, it does not have a velocity.
|
||||
return Double.compare(velocity.x, 0) != 0 || Double.compare(velocity.z, 0) != 0 || velocity.y > 0;
|
||||
} else {
|
||||
// The entity does not have velocity if the velocity is zero
|
||||
return !velocity.equals(Vector3d.zero());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isOnGround() {
|
||||
return onGround;
|
||||
}
|
||||
|
||||
public Vector3d getVelocity() {
|
||||
return velocity;
|
||||
}
|
||||
|
||||
public void setVelocity(Vector3d velocity) {
|
||||
this.velocity = velocity;
|
||||
sendPacketToViewers(getVelocityPacket());
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return location.getX();
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return location.getY();
|
||||
}
|
||||
|
||||
public double getZ() {
|
||||
return location.getZ();
|
||||
}
|
||||
|
||||
public float getYaw() {
|
||||
return location.getYaw();
|
||||
}
|
||||
|
||||
public float getPitch() {
|
||||
return location.getPitch();
|
||||
}
|
||||
|
||||
private WrapperPlayServerEntityVelocity getVelocityPacket() {
|
||||
|
@ -189,4 +242,8 @@ public class WrapperEntity {
|
|||
return new WrapperPlayServerEntityVelocity(entityId, velocity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(long time) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package me.tofaa.entitylib.entity;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
|
||||
import me.tofaa.entitylib.meta.EntityMeta;
|
||||
import me.tofaa.entitylib.EntityLib;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a {@link WrapperEntity} with goals, AI and pathfinding.
|
||||
* <p>
|
||||
* To create a new {@link WrapperEntityCreature} use {@link EntityLib#createEntityCreature(int, UUID, EntityType)} or {@link EntityLib#createEntityCreature(UUID, EntityType)}.
|
||||
* <br>
|
||||
* Creature entities require some sort of ticking mechanism on your server to work properly. They need to be dynamically updated every tick.
|
||||
* Goal and Target selectors are grouped into AIGroups, which are then added to the entity. The AIGroups are then updated every tick.
|
||||
* <br>
|
||||
* The {@link WrapperEntityCreature} can be inherited to create custom entities.
|
||||
* </p>
|
||||
*/
|
||||
public class WrapperEntityCreature extends WrapperLivingEntity{
|
||||
|
||||
public WrapperEntityCreature(int entityId, @NotNull UUID uuid, EntityType entityType, EntityMeta meta) {
|
||||
super(entityId, uuid, entityType, meta);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package me.tofaa.entitylib.entity;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
|
||||
import com.github.retrooper.packetevents.protocol.world.Location;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import me.tofaa.entitylib.meta.EntityMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class WrapperExperienceOrbEntity extends WrapperEntity {
|
||||
|
||||
private short experience;
|
||||
private Location slideTowards;
|
||||
|
||||
public WrapperExperienceOrbEntity(int entityId, @NotNull UUID uuid, EntityType entityType, EntityMeta meta) {
|
||||
super(entityId, uuid, entityType, meta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a slight slide motion towards the given location.
|
||||
* <p>
|
||||
* For this to work, this method needs to be called every tick until the entity reaches the location.
|
||||
* We don't have ticking or updating in this library, so you'll have to do it yourself.
|
||||
* This is an attempt to mimmick the vanilla behavior.
|
||||
* </p>
|
||||
*/
|
||||
public void updateSliding() {
|
||||
if (hasNoGravity()) {
|
||||
setVelocity(getVelocity().add(0, -0.3f, 0));
|
||||
}
|
||||
|
||||
double d = 8.0;
|
||||
Vector3d distance = new Vector3d(slideTowards.getX() - getX(), slideTowards.getY() - getY(), slideTowards.getZ() - getZ());
|
||||
double length = distance.length();
|
||||
if (length < 8.0) {
|
||||
double f = 1 - (length / 8);
|
||||
setVelocity(getVelocity().add(distance.normalize().multiply(f * f * 0.1)));
|
||||
}
|
||||
float g = 0.98f;
|
||||
if (this.isOnGround()) {
|
||||
g = 0.6f * 0.98f;
|
||||
}
|
||||
setVelocity(getVelocity().multiply(g, 0.98f, g));
|
||||
if (isOnGround()) {
|
||||
setVelocity(getVelocity().multiply(1, -0.9f, 1));
|
||||
}
|
||||
}
|
||||
|
||||
public Location getSlideTowards() {
|
||||
return slideTowards;
|
||||
}
|
||||
|
||||
public void setSlideTowards(Location slideTowards) {
|
||||
this.slideTowards = slideTowards;
|
||||
}
|
||||
|
||||
public short getExperience() {
|
||||
return experience;
|
||||
}
|
||||
|
||||
public void setExperience(short experience) {
|
||||
getViewers().forEach(this::removeViewer);
|
||||
this.experience = experience;
|
||||
getViewers().forEach(this::addViewer);
|
||||
}
|
||||
|
||||
}
|
|
@ -2,6 +2,7 @@ package me.tofaa.entitylib.entity;
|
|||
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
|
||||
import me.tofaa.entitylib.meta.EntityMeta;
|
||||
import me.tofaa.entitylib.meta.types.LivingEntityMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -9,14 +10,36 @@ import java.util.UUID;
|
|||
public class WrapperLivingEntity extends WrapperEntity{
|
||||
|
||||
private final WrapperEntityEquipment equipment;
|
||||
private float maxHealth;
|
||||
|
||||
public WrapperLivingEntity(int entityId, @NotNull UUID uuid, EntityType entityType, EntityMeta meta) {
|
||||
super(entityId, uuid, entityType, meta);
|
||||
this.equipment = new WrapperEntityEquipment(this);
|
||||
}
|
||||
|
||||
|
||||
public float getHealth() {
|
||||
return getMeta().getHealth();
|
||||
}
|
||||
|
||||
public void setHealth(float health) {
|
||||
getMeta().setHealth(Math.min(Math.max(health, 0), getMaxHealth()));
|
||||
}
|
||||
|
||||
public float getMaxHealth() {
|
||||
return maxHealth;
|
||||
}
|
||||
|
||||
public void setMaxHealth(float maxHealth) {
|
||||
this.maxHealth = maxHealth;
|
||||
}
|
||||
|
||||
public WrapperEntityEquipment getEquipment() {
|
||||
return equipment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LivingEntityMeta getMeta() {
|
||||
return (LivingEntityMeta) super.getMeta();
|
||||
}
|
||||
}
|
||||
|
|
4
src/main/java/me/tofaa/entitylib/entity/ai/AIGroup.java
Normal file
4
src/main/java/me/tofaa/entitylib/entity/ai/AIGroup.java
Normal file
|
@ -0,0 +1,4 @@
|
|||
package me.tofaa.entitylib.entity.ai;
|
||||
|
||||
public interface AIGroup {
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package me.tofaa.entitylib.entity.ai;
|
||||
|
||||
public class GoalSelector {
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package me.tofaa.entitylib.entity.ai;
|
||||
|
||||
public class TargetSelector {
|
||||
}
|
22
src/main/java/me/tofaa/entitylib/world/WrapperWorld.java
Normal file
22
src/main/java/me/tofaa/entitylib/world/WrapperWorld.java
Normal file
|
@ -0,0 +1,22 @@
|
|||
package me.tofaa.entitylib.world;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import me.tofaa.entitylib.entity.WrapperEntity;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class WrapperWorld {
|
||||
|
||||
private final UUID uuid;
|
||||
private final Set<WrapperEntity> entities;
|
||||
private final Set<User> players;
|
||||
|
||||
public WrapperWorld(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
this.entities = new HashSet<>();
|
||||
this.players = new HashSet<>();
|
||||
}
|
||||
|
||||
}
|
|
@ -22,6 +22,7 @@ public final class EntityLibPlugin extends JavaPlugin {
|
|||
getCommand("testapi").setExecutor(new TestCommand());
|
||||
getCommand("testentity").setExecutor(new TestEntityCommand());
|
||||
getCommand("testdisplay").setExecutor(new TestDisplayCommand());
|
||||
getCommand("spawnclickablefrog").setExecutor(new SpawnClickableFrogCommand());
|
||||
instance = this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package me.tofaa.entitylib;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
|
||||
import me.tofaa.entitylib.entity.WrapperEntity;
|
||||
import me.tofaa.entitylib.meta.mobs.FrogMeta;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SpawnClickableFrogCommand implements CommandExecutor {
|
||||
|
||||
private final Map<WrapperEntity, BukkitTask> updateTasks = new HashMap<>();
|
||||
private final FrogMeta.Variant[] variants = FrogMeta.Variant.values();
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) {
|
||||
Player player = (Player) commandSender;
|
||||
WrapperEntity e = EntityLib.createEntity(UUID.randomUUID(), EntityTypes.FROG);
|
||||
FrogMeta meta = (FrogMeta) e.getMeta();
|
||||
meta.setHasGlowingEffect(true);
|
||||
meta.setCustomNameVisible(true);
|
||||
meta.setCustomName(Component.text("CLICK ME!"));
|
||||
updateTasks.put(e, Bukkit.getScheduler().runTaskTimerAsynchronously(
|
||||
EntityLibPlugin.instance,
|
||||
new Runnable() {
|
||||
int i = 0;
|
||||
Random random = new Random();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!e.hasSpawned()) return;
|
||||
int r = random.nextInt(2);
|
||||
meta.setVariant(variants[r]);
|
||||
meta.setCustomName(Component.text("CLICKED: " + i + " TIMES"));
|
||||
}
|
||||
},
|
||||
20, 20));
|
||||
e.addViewer(player.getUniqueId());
|
||||
e.spawn(SpigotConversionUtil.fromBukkitLocation(player.getLocation()));
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -14,3 +14,6 @@ commands:
|
|||
testdisplay:
|
||||
description: Test PEDisplay API
|
||||
usage: /<command>
|
||||
spawnclickablefrog:
|
||||
description: Spawn a clickable frog
|
||||
usage: /<command>
|
Loading…
Reference in a new issue