Skip to content

Commit

Permalink
Add support for FancyCharacters and enhance CitizensCharacter
Browse files Browse the repository at this point in the history
Introduce FancyCharacter and FancyCharacterController for FancyNpcs integration, enabling advanced NPC management. Extend CitizensCharacter functionality by adding new methods for teleportation, displaying names, and respawn handling. Improve CitizensCharacterController with streamlined NPC creation and capabilities.
  • Loading branch information
NonSwag committed Dec 21, 2024
1 parent b4dcb26 commit a88f023
Show file tree
Hide file tree
Showing 4 changed files with 457 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;

import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
Expand All @@ -28,7 +27,8 @@
@NullMarked
public class CitizensCharacterController implements CharacterController {
private final EnumSet<CharacterCapability> capabilities = EnumSet.of(
CharacterCapability.NON_PLAYER_ENTITIES
CharacterCapability.NON_PLAYER_ENTITIES,
CharacterCapability.SERVER_ENTITIES
);
private final ServicePlugin plugin;

Expand All @@ -38,7 +38,7 @@ public CitizensCharacterController(ServicePlugin plugin) {

@Override
public <T extends Entity> Character<T> createNPC(String name, Class<T> type) {
return createNPC(name, getType(type));
return createNPC(name, plugin.getEntityTypeByClass(type));
}

@Override
Expand All @@ -49,7 +49,7 @@ public <T extends Entity> Character<T> createNPC(String name, EntityType type) {

@Override
public <T extends Entity> Character<T> spawnNPC(String name, Location location, Class<T> type) {
return spawnNPC(name, location, getType(type));
return spawnNPC(name, location, plugin.getEntityTypeByClass(type));
}

@Override
Expand All @@ -64,12 +64,6 @@ public <T extends Entity> Optional<Character<T>> getNPC(T entity) {
.map(CitizensCharacter::new);
}

private EntityType getType(Class<? extends Entity> type) {
return Arrays.stream(EntityType.values())
.filter(entityType -> type.equals(entityType.getEntityClass()))
.findAny().orElseThrow();
}

@Override
public @Unmodifiable List<Character<?>> getNPCs() {
return streamNPCs().map(CitizensCharacter::new)
Expand All @@ -92,6 +86,14 @@ private EntityType getType(Class<? extends Entity> type) {
.collect(Collectors.toUnmodifiableList());
}

@Override
public Optional<Character<?>> getNPC(String name) {
return streamNPCs()
.filter(npc -> name.equals(npc.getRawName()))
.<Character<?>>map(CitizensCharacter::new)
.findAny();
}

@Override
public Character<Player> createNPC(String name) {
return createNPC(name, Player.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package net.thenextlvl.service.controller.character;

import de.oliver.fancynpcs.api.FancyNpcsPlugin;
import de.oliver.fancynpcs.api.NpcData;
import net.thenextlvl.service.ServicePlugin;
import net.thenextlvl.service.api.npc.Character;
import net.thenextlvl.service.api.npc.CharacterCapability;
import net.thenextlvl.service.api.npc.CharacterController;
import net.thenextlvl.service.model.character.fancy.FancyCharacter;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;

import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

@NullMarked
public class FancyCharacterController implements CharacterController {
private final EnumSet<CharacterCapability> capabilities = EnumSet.of(
CharacterCapability.NON_PLAYER_ENTITIES
);
private final ServicePlugin plugin;

public FancyCharacterController(ServicePlugin plugin) {
this.plugin = plugin;
}

@Override
public <T extends Entity> Character<T> createNPC(String name, Class<T> type) {
return createNPC(name, plugin.getEntityTypeByClass(type));
}

@Override
public <T extends Entity> Character<T> createNPC(String name, EntityType type) {
var plugin = FancyNpcsPlugin.get();

var location = new Location(this.plugin.getServer().getWorlds().getFirst(), 0, 0, 0);
var data = new NpcData(name, new UUID(0, 0), location);
data.setType(type);
var npc = plugin.getNpcAdapter().apply(data);

plugin.getNpcManager().registerNpc(npc);
npc.create();
return new FancyCharacter<>(npc);
}

@Override
public <T extends Entity> Character<T> spawnNPC(String name, Location location, Class<T> type) {
return spawnNPC(name, location, plugin.getEntityTypeByClass(type));
}

@Override
public <T extends Entity> Character<T> spawnNPC(String name, Location location, EntityType type) {
var npc = this.<T>createNPC(name, type);
npc.spawn(location);
return npc;
}

@Override
public <T extends Entity> Optional<Character<T>> getNPC(T entity) {
return Optional.empty();
}

@Override
public Character<Player> createNPC(String name) {
return createNPC(name, Player.class);
}

@Override
public Character<Player> spawnNPC(String name, Location location) {
return spawnNPC(name, location, Player.class);
}

@Override
public @Unmodifiable List<Character<?>> getNPCs() {
return FancyNpcsPlugin.get().getNpcManager().getAllNpcs().stream()
.map(FancyCharacter::new)
.collect(Collectors.toUnmodifiableList());
}

@Override
public @Unmodifiable List<Character<?>> getNPCs(Player player) {
return FancyNpcsPlugin.get().getNpcManager().getAllNpcs().stream()
.filter(npc -> npc.getIsVisibleForPlayer().containsKey(player.getUniqueId()))
.map(FancyCharacter::new)
.collect(Collectors.toUnmodifiableList());
}

@Override
public @Unmodifiable List<Character<?>> getNPCs(World world) {
return FancyNpcsPlugin.get().getNpcManager().getAllNpcs().stream()
.filter(npc -> npc.getData().getLocation() != null)
.filter(npc -> world.equals(npc.getData().getLocation().getWorld()))
.map(FancyCharacter::new)
.collect(Collectors.toUnmodifiableList());
}

@Override
public Optional<Character<?>> getNPC(String name) {
return Optional.ofNullable(FancyNpcsPlugin.get().getNpcManager().getNpc(name))
.map(FancyCharacter::new);
}

@Override
public Optional<Character<?>> getNPC(UUID uuid) {
return Optional.empty();
}

@Override
public Optional<Character<Player>> getNPC(Player player) {
return Optional.empty();
}

@Override
public @Unmodifiable EnumSet<CharacterCapability> getCapabilities() {
return EnumSet.copyOf(this.capabilities);
}

@Override
public boolean hasCapabilities(Collection<CharacterCapability> capabilities) {
return this.capabilities.containsAll(capabilities);
}

@Override
public boolean hasCapability(CharacterCapability capability) {
return this.capabilities.contains(capability);
}

@Override
public String getName() {
return "FancyNpcs";
}

@Override
public boolean isNPC(Entity entity) {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@

import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.trait.trait.MobType;
import net.citizensnpcs.api.trait.trait.PlayerFilter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.thenextlvl.service.api.npc.Character;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
Expand All @@ -18,6 +23,7 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@NullMarked
Expand Down Expand Up @@ -146,6 +152,11 @@ public Server getServer() {
return getEntity().map(Entity::getWorld).orElse(null);
}

@Override
public void delete() {
npc.destroy();
}

@Override
public double getX() {
return getEntity().map(Entity::getX).orElse(0d);
Expand All @@ -171,13 +182,33 @@ public float getYaw() {
return getEntity().map(Entity::getYaw).orElse(0f);
}

@Override
public CompletableFuture<Boolean> teleportAsync(Location location) {
return CompletableFuture.supplyAsync(() -> {
npc.teleport(location, PlayerTeleportEvent.TeleportCause.PLUGIN);
return true;
});
}

@Override
public Component getDisplayName() {
return MiniMessage.miniMessage().deserialize(npc.getRawName());
}

@Override
@SuppressWarnings("unchecked")
public Optional<T> getEntity() {
return Optional.ofNullable(npc.getEntity())
.map(entity -> (T) entity);
}

@Override
public EntityType getType() {
return npc.getTraitOptional(MobType.class)
.transform(MobType::getType)
.or(EntityType.PLAYER);
}

@Override
public boolean isProtected() {
return npc.isProtected();
Expand All @@ -194,19 +225,24 @@ public boolean isTablistEntryHidden() {
}

@Override
public boolean spawn() {
var location = getLocation();
return location != null && spawn(location);
public boolean spawn(Location location) {
return npc.spawn(location);
}

@Override
public boolean spawn(Location location) {
return npc.spawn(location);
public boolean remove() {
return npc.despawn();
}

@Override
public void remove() {
npc.destroy();
public boolean respawn() {
var location = getLocation();
return location != null && npc.despawn() && npc.spawn(location);
}

@Override
public void setDisplayName(Component displayName) {
npc.setName(MiniMessage.miniMessage().serialize(displayName));
}

@Override
Expand Down
Loading

0 comments on commit a88f023

Please sign in to comment.