Start implementations

This commit is contained in:
Paul Reilly 2023-06-01 23:02:01 -05:00
parent def84bd747
commit a632eb778c
11 changed files with 535 additions and 8 deletions

View File

@ -0,0 +1,28 @@
package me.totalfreedom.fossil.items;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public final class ClownfishItem extends ShopItem
{
public ClownfishItem()
{
super(Material.TROPICAL_FISH);
}
@Override
public void runAction(final @NotNull Player user, final @Nullable Entity target)
{
if (target == null) return;
final Location location = user.getEyeLocation().clone();
final Vector vector = location.getDirection().multiply(2);
target.setVelocity(vector);
}
}

View File

@ -0,0 +1,34 @@
package me.totalfreedom.fossil.items;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class ShopItem
{
private final ItemStack item;
private final ItemMeta meta;
protected ShopItem(final Material material)
{
this.item = new ItemStack(material, 1);
this.meta = this.item.getItemMeta();
}
public abstract void runAction(@NotNull final Player user, @Nullable final Entity target);
public ItemStack getItem()
{
return this.item;
}
public ItemMeta getMeta()
{
return this.meta;
}
}

View File

@ -0,0 +1,21 @@
package me.totalfreedom.fossil.items;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public final class TrailItem extends ShopItem
{
public TrailItem(final Material material)
{
super(material);
}
@Override
public void runAction(final @NotNull Player user, final @Nullable Entity target)
{
}
}

View File

@ -0,0 +1,41 @@
package me.totalfreedom.fossil.trail;
import me.totalfreedom.particle.Trail;
import me.totalfreedom.particle.TrailType;
import me.totalfreedom.service.Service;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class Trailer extends Service
{
private final List<Trail> trailList = new ArrayList<>();
public Trailer() {
super("trailer_service");
}
public void addTrail(final Trail trail) {
this.trailList.add(trail);
}
public void removeTrail(final Trail trail) {
this.trailList.remove(trail);
}
@Override
public void tick()
{
for (final Trail trail : trailList) {
if (trail.getAssociatedPlayer().isOnline()) {
final Player player = (Player) trail.getAssociatedPlayer();
}
}
}
}

View File

@ -0,0 +1,32 @@
package me.totalfreedom.fossil.trail.types;
import me.totalfreedom.particle.TrailType;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
public final class BasicTrail extends SimpleTrail
{
protected BasicTrail(final Player player)
{
super(player, TrailType.DEFAULT);
super.setColor(Color.RED);
}
@Override
public void spawnParticle()
{
// Exit immediately if either condition is false.
if (!isActive() || !getAssociatedPlayer().isOnline()) return;
// Trail is active and the player is online.
final Particle particle = getTrailType().getType();
final Particle.DustOptions options = new Particle.DustOptions(getColor(), 3);
final Player player = (Player) getAssociatedPlayer();
final Location location = player.getLocation()
.clone()
.subtract(0, 1, 0);
location.getWorld().spawnParticle(particle, location, 1, 0.0, 0.5, 0.0, options);
}
}

View File

@ -0,0 +1,46 @@
package me.totalfreedom.fossil.trail.types;
import me.totalfreedom.particle.TrailType;
import me.totalfreedom.utils.InterpolationUtils;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
import java.util.Iterator;
public final class RainbowTrail extends SimpleTrail
{
private Iterator<Color> currentColor;
protected RainbowTrail(final Player player)
{
super(player, TrailType.DEFAULT);
setColors(InterpolationUtils.rainbow(40 % 7));
this.currentColor = getColors().iterator();
}
@Override
public void spawnParticle()
{
// Exit immediately if either case is false.
if (!isActive() || !getAssociatedPlayer().isOnline()) return;
// Re-initialize the color iterator if the iterator has previously reached the end of its index.
if (!currentColor.hasNext())
{
this.currentColor = getColors().iterator();
}
final Color color = currentColor.next();
final Player player = (Player) getAssociatedPlayer();
final Particle particle = getTrailType().getType();
final Particle.DustOptions options = new Particle.DustOptions(color, 3);
final Location location = player.getLocation()
.clone()
.subtract(0, 1, 0);
location.getWorld()
.spawnParticle(particle, location, 1, 0.0, 0.5, 0.0, options);
}
}

View File

@ -0,0 +1,90 @@
package me.totalfreedom.fossil.trail.types;
import me.totalfreedom.particle.Trail;
import me.totalfreedom.particle.TrailType;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
import java.util.UUID;
public abstract class SimpleTrail implements Trail
{
private final UUID associatedPlayerUUID;
private final TrailType trailType;
private Color staticColor = null;
private Set<Color> gradientColor = null;
private boolean active = false;
protected SimpleTrail(final Player player, final TrailType trailType) {
this.associatedPlayerUUID = player.getUniqueId();
this.trailType = trailType;
}
@Override
public @NotNull UUID getAssociatedPlayerUUID()
{
return associatedPlayerUUID;
}
@Override
public @NotNull OfflinePlayer getAssociatedPlayer()
{
return Bukkit.getOfflinePlayer(getAssociatedPlayerUUID());
}
@Override
public @NotNull TrailType getTrailType()
{
return trailType;
}
@Override
public @Nullable Color getColor()
{
return staticColor;
}
@Override
public void setColor(@NotNull final Color color)
{
this.gradientColor = null;
this.staticColor = color;
}
@Override
public @Nullable Set<Color> getColors()
{
return this.gradientColor;
}
@Override
public void setColors(@NotNull final Set<Color> colors)
{
this.staticColor = null;
this.gradientColor = colors;
}
@Override
public boolean isGradient()
{
return gradientColor != null;
}
@Override
public boolean isActive()
{
return active;
}
@Override
public void setActive(final boolean active)
{
this.active = active;
}
}

View File

@ -0,0 +1,12 @@
package me.totalfreedom.fossil.trail.types;
import org.bukkit.entity.Player;
public final class TrailProvider
{
public BasicTrail basicTrail(final Player player) {
return new BasicTrail(player);
}
}

View File

@ -0,0 +1,7 @@
package me.totalfreedom.api;
@FunctionalInterface
public interface Interpolator
{
double[] interpolate(final double from, final double to, final int max);
}

View File

@ -1,31 +1,127 @@
package me.totalfreedom.particle;
import me.totalfreedom.api.Interpolator;
import me.totalfreedom.utils.InterpolationUtils;
import org.bukkit.Color;
import org.bukkit.entity.Player;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.Particle;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
import java.util.UUID;
/**
* Represents a Trail instance for a specific player.
*/
public interface Trail
{
/**
* Returns the UUID of the player associated with the trail. This is for usage with our persistant storage
* container so that we can safely send and retrieve the trails without having to directly reference a player
* object.
* <br>
* TL;DR Memory optimization!
*
* @return The UUID of the player associated with this trail.
*/
@NotNull
UUID getAssocPlayerUUID();
UUID getAssociatedPlayerUUID();
// Nullable because the player may not be online and trail selections should be persistent whether they are
// active or not.
@Nullable
Player getAssocPlayer();
/**
* Returns the player associated with this trail. Trails are user specific, and should be persistent across all
* usages. This is also used when displaying the particles, as they will be relative to the player's back, which
* is an inverse offset of the player's eye location. We use OfflinePlayer as we can make a simple check and cast
* to determine if the player is online when spawning trails.
*
* @return The player associated with this Trail.
*/
@NotNull
OfflinePlayer getAssociatedPlayer();
/**
* Gets the Trail Type of this trail. This is used to determine what type of trail this is, and what
* {@link Particle} it should use.
*
* @return The Trail Type of this trail.
* @see TrailType
*/
@NotNull
TrailType getTrailType();
@NotNull
/**
* This method is nullable because if the value of {@link #isGradient()} is true, then
* {@link #getColors()} should be used instead, as that will contain the color data for our trail.
* <br>
* However, this method will also be null if the particle type is not colorable.
*
* @return The color of the trail, or null if the trail is a gradient or non-colorable.
* @see Particle
* @see #getColors();
*/
@Nullable
Color getColor();
/**
* Sets the static color of the trail. If you are trying to use a gradient, use {@link #setColors(Set)} instead.
* <br>
*
* @param color The color to set the trail to.
*/
void setColor(@NotNull Color color);
/**
* This method is nullable because if the value of {@link #isGradient()} is false, then
* {@link #getColor()} should be used instead, as our trail is a single static color.
* <br>
* However, this method will also be null if the particle type is not colorable.
*
* @return The colors of the trail, or null if the trail is not a gradient or non-colorable.
* @see #getColor()
* @see Particle
* @see InterpolationUtils
* @see Interpolator
*/
@Nullable
Set<Color> getColors();
/**
* Sets the colors of the trail. If you are trying to use a static color, use {@link #setColor(Color)} instead.
* <br>
* This should be used for trails that iterate over a set of colors, such as a rainbow trail.
*
* @param colors The colors to set the trail to. It is recommended to use {@link InterpolationUtils} to generate
* interpolated gradients for this.
*/
void setColors(@NotNull Set<Color> colors);
/**
* Validates whether this Trail is a gradient or a static trail.
* <br>
* This is entirely based on whether {@link #getColors()} returns null or not.
*
* @return True if {@link #getColors()} is not null, false otherwise.
*/
boolean isGradient();
/**
* Gets whether the trail is active.
*
* @return True if the trail is active, false if it is not.
*/
boolean isActive();
void setActive(boolean active);
/**
* Turn the trail on or off.
*
* @param active True if the trail should be active, false if it should not.
*/
void setActive(final boolean active);
/**
* Spawns a particle (if gradient, the next particle) on the supplied location object.
*/
void spawnParticle();
}

View File

@ -0,0 +1,120 @@
package me.totalfreedom.utils;
import me.totalfreedom.api.Interpolator;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Color;
import java.util.LinkedHashSet;
import java.util.Set;
public class InterpolationUtils
{
public static Set<Color> rainbow(final int length)
{
final LinkedHashSet<Color> base = new LinkedHashSet<>();
final Set<Color> redToOrange = hsvGradient(length, Color.RED, Color.ORANGE, InterpolationUtils::linear);
final Set<Color> orangeToYellow = hsvGradient(length, Color.ORANGE, Color.YELLOW, InterpolationUtils::linear);
final Set<Color> yellowToGreen = hsvGradient(length, Color.YELLOW, Color.GREEN, InterpolationUtils::linear);
final Set<Color> greenToBlue = hsvGradient(length, Color.GREEN, Color.BLUE, InterpolationUtils::linear);
final Set<Color> blueToPurple = hsvGradient(length, Color.BLUE, Color.PURPLE, InterpolationUtils::linear);
final Set<Color> purpleToRed = hsvGradient(length, Color.PURPLE, Color.RED, InterpolationUtils::linear);
base.addAll(redToOrange);
base.addAll(orangeToYellow);
base.addAll(yellowToGreen);
base.addAll(greenToBlue);
base.addAll(blueToPurple);
base.addAll(purpleToRed);
return base;
}
private static Set<Color> hsvGradient(final int length, final Color from, final Color to,
final Interpolator interpolator)
{
// returns a float-array where hsv[0] = hue, hsv[1] = saturation, hsv[2] = value/brightness
final float[] hsvFrom = java.awt.Color.RGBtoHSB(from.getRed(), from.getGreen(), from.getBlue(), null);
final float[] hsvTo = java.awt.Color.RGBtoHSB(to.getRed(), to.getGreen(), to.getBlue(), null);
final double[] h = interpolator.interpolate(hsvFrom[0], hsvTo[0], length);
final double[] s = interpolator.interpolate(hsvFrom[1], hsvTo[1], length);
final double[] v = interpolator.interpolate(hsvFrom[2], hsvTo[2], length);
final LinkedHashSet<Color> gradient = new LinkedHashSet<>();
for (int i = 0; i < length; i++)
{
final int rgb = java.awt.Color.HSBtoRGB((float) h[i], (float) s[i], (float) v[i]);
final Color color = Color.fromRGB(rgb);
gradient.add(color);
}
return gradient;
}
private static double[] linear(final double from, final double to, final int max)
{
final double[] res = new double[max];
for (int i = 0; i < max; i++)
{
res[i] = from + i * ((to - from) / (max - 1));
}
return res;
}
public static Set<Color> rgbGradient(final int length, final Color from, final Color to,
final Interpolator interpolator)
{
final double[] r = interpolator.interpolate(from.getRed(), to.getRed(), length);
final double[] g = interpolator.interpolate(from.getGreen(), to.getGreen(), length);
final double[] b = interpolator.interpolate(from.getBlue(), to.getBlue(), length);
final LinkedHashSet<Color> gradient = new LinkedHashSet<>();
for (int i = 0; i < length; i++)
{
final Color color = Color.fromRGB((int) r[i], (int) g[i], (int) b[i]);
gradient.add(color);
}
return gradient;
}
public static Set<TextColor> rainbowComponent(final int length)
{
final LinkedHashSet<TextColor> base = new LinkedHashSet<>();
final Set<TextColor> redToOrange = componentRGBGradient(length, NamedTextColor.RED,
NamedTextColor.GOLD, InterpolationUtils::linear);
final Set<TextColor> orangeToYellow = componentRGBGradient(length, NamedTextColor.GOLD,
NamedTextColor.YELLOW, InterpolationUtils::linear);
final Set<TextColor> yellowToGreen = componentRGBGradient(length, NamedTextColor.YELLOW,
NamedTextColor.GREEN, InterpolationUtils::linear);
final Set<TextColor> greenToBlue = componentRGBGradient(length, NamedTextColor.GREEN,
NamedTextColor.BLUE, InterpolationUtils::linear);
final Set<TextColor> blueToPurple = componentRGBGradient(length, NamedTextColor.BLUE,
NamedTextColor.LIGHT_PURPLE, InterpolationUtils::linear);
final Set<TextColor> purpleToRed = componentRGBGradient(length, TextColor.color(75, 0, 130),
TextColor.color(255, 0, 0), InterpolationUtils::linear);
base.addAll(redToOrange);
base.addAll(orangeToYellow);
base.addAll(yellowToGreen);
base.addAll(greenToBlue);
base.addAll(blueToPurple);
base.addAll(purpleToRed);
return base;
}
public static Set<TextColor> componentRGBGradient(final int length, final TextColor from, final TextColor to,
final Interpolator interpolator)
{
final double[] r = interpolator.interpolate(from.red(), to.red(), length);
final double[] g = interpolator.interpolate(from.green(), to.green(), length);
final double[] b = interpolator.interpolate(from.blue(), to.blue(), length);
final LinkedHashSet<TextColor> gradient = new LinkedHashSet<>();
for (int i = 0; i < length; i++)
{
final TextColor color = TextColor.color((int) r[i], (int) g[i], (int) b[i]);
gradient.add(color);
}
return gradient;
}
}