Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ plugins are installed.

- Use a configured egg, name the pet in chat, and confirm. The egg is spent
only after the name is confirmed and there is room in the outside quota.
- Right-click the pet with an empty hand for the care screen. Food, a brush,
and medicine act immediately. Anyone nearby can feed, clean, and heal. Play,
sleep, tricks, and the whistle belong to the owner.
- Sneak and right-click the pet with an empty hand for its info screen. A
plain right-click checks on the pet: it tells you what is wrong, or you pet
it when it is fine. Pets only sit through the trained Sit trick. A worn-out
pet lies down on its own and recovers energy while it sleeps. Food, a
brush, and medicine act immediately. Anyone nearby can feed, clean, and heal. Play,
tricks, and the whistle belong to the owner.
- Sneak and right-click with the kennel item to place a kennel. The kennel and
the whistle open the same list: take a pet out, call one, store one, or
rename it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public void onDisable() {
autosave.cancel();
}
if (actions != null) {
actions.holograms().clear();
actions.stashLooseToys();
}
if (store != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@ public static String line(String message) {
return message.trim();
}

public static String key(String message) {
return line(message)
.toLowerCase(Locale.ROOT)
.replaceAll("^[\\p{Punct}\\s]+|[\\p{Punct}\\s]+$", "")
.replaceAll("\\s+", " ");
}

public static boolean matches(String message, String word) {
if (word == null || word.isBlank()) {
return false;
}
return line(message).toLowerCase(Locale.ROOT).equals(word.trim().toLowerCase(Locale.ROOT));
return key(message).equals(key(word));
}
}
57 changes: 53 additions & 4 deletions src/main/java/net/tfminecraft/companionpets/fx/PetFx.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import org.bukkit.util.Vector;

import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;

public final class PetFx {
private static final long REPEAT_AFTER_MILLIS = 1_600L;
Expand All @@ -30,26 +32,47 @@ private PetFx() {
}

public static void bar(Player player, String text) {
bar(player, Component.text(text));
}

public static void bar(Player player, Component text) {
show(player, text);
QUIET.put(player.getUniqueId(), System.currentTimeMillis() + HOLD_MILLIS);
}

public static void tell(Player player, String text) {
player.sendMessage(Component.text("✦ ", NamedTextColor.GOLD).append(Component.text(text, NamedTextColor.YELLOW)));
}

public static void tip(Player player, String text) {
player.sendMessage(Component.text(" Tip: ", NamedTextColor.AQUA).append(Component.text(text, NamedTextColor.GRAY)));
}

public static void cue(Player player, Sound sound, float pitch) {
player.playSound(player.getLocation(), sound, 0.7f, pitch);
}

public static void status(Player player, String text) {
status(player, Component.text(text));
}

public static void status(Player player, Component text) {
Long until = QUIET.get(player.getUniqueId());
if (until != null && System.currentTimeMillis() < until) {
return;
}
show(player, text);
}

private static void show(Player player, String text) {
private static void show(Player player, Component text) {
long now = System.currentTimeMillis();
String key = PlainTextComponentSerializer.plainText().serialize(text);
Hold hold = HOLDS.get(player.getUniqueId());
if (hold != null && text.equals(hold.text) && now - hold.at < REPEAT_AFTER_MILLIS) {
if (hold != null && key.equals(hold.text) && now - hold.at < REPEAT_AFTER_MILLIS) {
return;
}
HOLDS.put(player.getUniqueId(), new Hold(text, now));
player.sendActionBar(Component.text(text));
HOLDS.put(player.getUniqueId(), new Hold(key, now));
player.sendActionBar(text);
}

public static void ambient(Entity entity) {
Expand Down Expand Up @@ -96,6 +119,12 @@ public static void beg(Entity entity) {
}
}

public static void stopBeg(Entity entity) {
if (entity instanceof Wolf wolf && wolf.isValid()) {
wolf.setInterested(false);
}
}

public static void jump(Entity entity, boolean partial) {
Vector velocity = entity.getVelocity();
velocity.setY(partial ? 0.28 : 0.48);
Expand All @@ -111,6 +140,26 @@ public static void look(Entity entity, Location target) {
private record Hold(String text, long at) {
}

public static void happy(Entity entity, boolean loud) {
Sound sound = switch (entity.getType()) {
case WOLF -> loud ? Sound.ENTITY_WOLF_AMBIENT : Sound.ENTITY_WOLF_PANT;
case CAT -> loud ? Sound.ENTITY_CAT_PURREOW : Sound.ENTITY_CAT_PURR;
case FOX -> loud ? Sound.ENTITY_FOX_AMBIENT : Sound.ENTITY_FOX_SNIFF;
default -> ambientSound(entity.getType());
};
entity.getWorld().playSound(entity.getLocation(), sound, 0.9f, 1.1f);
}

public static void sad(Entity entity) {
Sound sound = switch (entity.getType()) {
case WOLF -> Sound.ENTITY_WOLF_WHINE;
case CAT -> Sound.ENTITY_CAT_BEG_FOR_FOOD;
case FOX -> Sound.ENTITY_FOX_SNIFF;
default -> ambientSound(entity.getType());
};
entity.getWorld().playSound(entity.getLocation(), sound, 0.8f, 0.9f);
}

public static Sound ambientSound(EntityType type) {
return switch (type) {
case WOLF -> Sound.ENTITY_WOLF_AMBIENT;
Expand Down
98 changes: 98 additions & 0 deletions src/main/java/net/tfminecraft/companionpets/fx/PetHolograms.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package net.tfminecraft.companionpets.fx;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitTask;

import net.kyori.adventure.text.Component;

public final class PetHolograms {
private static final long FOLLOW_TICKS = 2L;
private static final double ABOVE_NAME = 0.25;

private final Plugin plugin;
private final Map<UUID, Hologram> shown = new HashMap<>();

public PetHolograms(Plugin plugin) {
this.plugin = plugin;
}

public void show(Entity pet, Component text, long ticks) {
if (pet == null || !pet.isValid()) {
return;
}
UUID id = pet.getUniqueId();
remove(id);
ArmorStand stand = pet.getWorld().spawn(above(pet), ArmorStand.class, as -> {
as.customName(text);
as.setCustomNameVisible(true);
as.setVisible(false);
as.setMarker(true);
as.setGravity(false);
as.setSmall(true);
as.setInvulnerable(true);
as.setSilent(true);
as.setPersistent(false);
});
Hologram hologram = new Hologram(stand);
shown.put(id, hologram);
hologram.follow = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
if (!pet.isValid() || !stand.isValid()) {
remove(id, hologram);
return;
}
stand.teleport(above(pet));
}, FOLLOW_TICKS, FOLLOW_TICKS);
hologram.expire = Bukkit.getScheduler().runTaskLater(plugin, () -> remove(id, hologram), ticks);
}

public void clear() {
for (UUID id : List.copyOf(shown.keySet())) {
remove(id);
}
}

private void remove(UUID id) {
Hologram hologram = shown.get(id);
if (hologram != null) {
remove(id, hologram);
}
}

private void remove(UUID id, Hologram hologram) {
if (shown.get(id) == hologram) {
shown.remove(id);
}
if (hologram.follow != null) {
hologram.follow.cancel();
}
if (hologram.expire != null) {
hologram.expire.cancel();
}
if (hologram.stand.isValid()) {
hologram.stand.remove();
}
}

private static Location above(Entity pet) {
return pet.getLocation().add(0, pet.getHeight() + ABOVE_NAME, 0);
}

private static final class Hologram {
private final ArmorStand stand;
private BukkitTask follow;
private BukkitTask expire;

private Hologram(ArmorStand stand) {
this.stand = stand;
}
}
}
Loading