Documentation, Add BouncyPads

This commit is contained in:
Paul Reilly
2023-06-08 10:02:20 -05:00
parent 27dafd69e6
commit 1ea106d999
43 changed files with 2069 additions and 110 deletions

View File

@ -18,7 +18,7 @@ public class Fossil extends JavaPlugin
registration.getModuleRegistry()
.addModule(this);
registration.getServiceRegistry()
registration.getServiceTaskRegistry()
.registerService(
SubscriptionProvider.syncService(this, trailer));
}

View File

@ -0,0 +1,211 @@
package me.totalfreedom.fossil.bouncypads;
import com.google.errorprone.annotations.Immutable;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import java.util.SplittableRandom;
/**
* Represents a bouncy pad. Has a velocity and a type.
*/
@Immutable
public class BouncyPad
{
/**
* The velocity of the pad.
*/
private final double velocity;
/**
* The type of the pad.
*/
private final PadType padType;
/**
* Creates a new bouncy pad.
*
* @param velocity The velocity of the pad.
* @param padType The type of the pad.
*/
public BouncyPad(final double velocity, final PadType padType)
{
this.velocity = velocity;
this.padType = padType;
}
/**
* Creates a new bouncy pad with a type of {@link PadType#NORMAL}.
*
* @param velocity The velocity of the pad.
*/
public BouncyPad(final double velocity)
{
this(velocity, PadType.NORMAL);
}
/**
* Creates a new bouncy pad with a velocity of 1.1 and a type of {@link PadType#NORMAL}.
*/
public BouncyPad()
{
this(1.0 + 0.1F);
}
/**
* This method will bounce the player based on the type of the pad.
* <p>
* The type of the pad, defined by {@link #padType}, will determine how the player is bounced.
* <br>
* For type {@link PadType#NORMAL}, the player will be bounced if the face is {@link BlockFace#UP}.
* <br>
* For type {@link PadType#SIDES}, the player will be bounced if the face is not {@link BlockFace#UP} or
* {@link BlockFace#DOWN}.
* <br>
* For type {@link PadType#ALL}, the player will be bounced regardless of the face.
* <br>
* For type {@link PadType#EXTREME}, the player will be bounced with a velocity based on the formula:
* <br>
* <span color=#f07a21><code>(((173.31 + 0.5 * velocity) - (31.2 + 0.5 * Math.pow(velocity, 2.0)) + (0.5 *
* Math.pow(velocity, 3.0))) - 173.31) / (velocity * (velocity - 1))</code></span>
* <br>
* For type {@link PadType#SPACE_CADET}, the player will be bounced with a velocity based on the formula:
* <br>
* <span color=#f07a21><code>Math.round(Math.abs((accel * 100.0) + Math.pow(y, Math.floor(accel)) /
* Math.exp(accel)))</code></span>
* <br>
* where <span color=#f07a21><code>y = Math.abs(random.nextGaussian(12, 5) * 0.5 + 0.5)</code></span> and <span
* color=#f07a21><code>accel = Math.sqrt(2 * 9.81 * y)</code></span>
* <br>
* <br>
* <b>NOTE:</b> The velocity of the pad is added with the inverse velocity of the player. The inverse
* velocity of the player is acquired by multiplying the velocity of the player by -1.
*
* @param player The player to bounce.
* @param face The face of the block the player is bouncing on.
*/
public void bouncePad(final Player player, final BlockFace face)
{
switch (padType)
{
case NORMAL -> bounceNormal(player, face);
case SIDES -> bounceSides(player, face);
case ALL -> bounceAll(player, face);
case EXTREME -> bounceExtreme(player, face);
case SPACE_CADET -> bounceSpaceCadet(player, face);
}
}
/**
* This method returns a vector based on the following:
* <br>
* <span color=#f07a21><code>(BlockFace direction + Player velocity * -1) * velocity</code></span>
* <br>
* <br>
* We retrieve a vector representing the direction in which this block face is facing. This is then added with the
* inverse velocity of the player, which is the direction and speed in which the player is moving multiplied by -1.
* This is then multiplied by the velocity of the pad.
*
* @param player The moving player
* @param face The face of the block the player is bouncing on.
* @return A vector representing the direction and speed in which the player should be bounced.
*/
private Vector getVector(final Player player, final BlockFace face)
{
return face.getDirection()
.add(player.getVelocity()
.multiply(-1))
.multiply(velocity);
}
/**
* This method will bounce the player if the face is {@link BlockFace#UP}.
*
* @param player The player to bounce.
* @param face The face of the block the player is bouncing on.
*/
private void bounceNormal(final Player player, final BlockFace face)
{
if (!face.equals(BlockFace.UP))
return;
player.setVelocity(getVector(player, face));
}
/**
* This method will bounce the player if the face is not {@link BlockFace#UP} or {@link BlockFace#DOWN}.
*
* @param player The player to bounce.
* @param face The face of the block the player is bouncing on.
*/
private void bounceSides(final Player player, final BlockFace face)
{
if (face == BlockFace.UP || face == BlockFace.DOWN)
return;
player.setVelocity(getVector(player, face));
}
/**
* This method will bounce the player regardless of the face.
*
* @param player The player to bounce.
* @param face The face of the block the player is bouncing on.
*/
private void bounceAll(final Player player, final BlockFace face)
{
player.setVelocity(getVector(player, face));
}
/**
* This method will bounce the player with a velocity based on the formula:
* <br>
* <span color=#f07a21><code>(((173.31 + 0.5 * velocity) - (31.2 + 0.5 * Math.pow(velocity, 2.0)) + (0.5 *
* Math.pow(velocity, 3.0))) - 173.31) / (velocity * (velocity - 1))</code></span>
* <br>
* <br>
* <b>NOTE:</b> The velocity of the pad is added with the inverse velocity of the player. The inverse
* velocity of the player is acquired by multiplying the velocity of the player by -1.
*
* @param player The player to bounce.
* @param face The face of the block the player is bouncing on.
*/
private void bounceExtreme(final Player player, final BlockFace face)
{
final double extremeVelocity = (((173.31 + 0.5 * velocity) - (31.2 + 0.5 * Math.pow(velocity, 2.0)) + (0.5 * Math.pow(velocity, 3.0))) - 173.31) / (velocity * (velocity - 1));
player.setVelocity(face.getDirection()
.add(player.getVelocity()
.multiply(-1))
.multiply(extremeVelocity * velocity));
}
/**
* This method will bounce the player with a velocity based on the formula:
* <br>
* <span color=#f07a21><code>Math.round(Math.abs((accel * 100.0) + Math.pow(y, Math.floor(accel)) /
* Math.exp(accel)))</code></span>
* <br>
* where <span color=#f07a21><code>y = Math.abs(random.nextGaussian(12, 5) * 0.5 + 0.5)</code></span> and
* <span color=#f07a21><code>accel = Math.sqrt(2 * 9.81 * y)</code></span>
*
* @param player The player to bounce.
* @param face The face of the block the player is bouncing on.
*/
private void bounceSpaceCadet(final Player player, final BlockFace face)
{
final SplittableRandom random = new SplittableRandom();
final double y = Math.abs(random.nextGaussian(12, 5) * 0.5 + 0.5);
final double accel = Math.sqrt(2 * 9.81 * y);
final double spaceVelocity = Math.round(Math.abs((accel * 100.0) + Math.pow(y, Math.floor(accel)) / Math.exp(accel)));
final Vector accelVector = new Vector(0, y + accel, 0);
final Vector postVector = new Vector(0, spaceVelocity, 0);
final Vector spaceVector = face.getDirection()
.add(player.getVelocity()
.multiply(-1))
.multiply(accelVector.multiply(postVector));
player.setVelocity(spaceVector);
}
}

View File

@ -0,0 +1,156 @@
package me.totalfreedom.fossil.bouncypads;
import me.totalfreedom.base.CommonsBase;
import me.totalfreedom.fossil.Fossil;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Tag;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Stream;
/**
* Holds all the active pads for each player, and also manages player pad interaction.
*/
public class PadHolder implements Listener
{
/**
* A map of all the currently active pads, stored by {@link Player} {@link UUID}.
*/
private final Map<UUID, BouncyPad> pads = new HashMap<>();
/**
* Creates a new pad holder.
*/
public PadHolder()
{
Bukkit.getPluginManager().registerEvents(this, CommonsBase
.getInstance()
.getRegistrations()
.getModuleRegistry()
.getProvider(Fossil.class)
.getModule());
}
/**
* Adds a pad for the given player. If the player already has a pad stored in the map,
* it will be overwritten with the new pad.
*
* @param player The player to add the pad for.
* @param pad The pad to add.
*/
public void addPad(final Player player, final BouncyPad pad)
{
this.pads.put(player.getUniqueId(), pad);
}
/**
* Removes the pad for the given player, if the player has one.
*
* @param player The player to remove the pad for.
*/
public void removePad(final Player player)
{
this.pads.remove(player.getUniqueId());
}
/**
* Gets the pad for the given player, if the player has one.
* If the player has no active pad, this will return null.
*
* @param player The player to get the pad for.
* @return The pad for the given player.
*/
@Nullable
public BouncyPad getPad(final Player player)
{
return this.pads.get(player.getUniqueId());
}
/**
* Checks if there is a pad active for the given player.
*
* @param player The player to check.
* @return True if the player has a pad, false otherwise.
*/
public boolean hasPad(final Player player)
{
return this.pads.containsKey(player.getUniqueId());
}
/**
* Gets a map of all the currently active pads, stored by {@link Player} {@link UUID}.
*
* @return A map of all the currently active pads.
*/
public Map<UUID, BouncyPad> getPads()
{
return this.pads;
}
/**
* Handles player pad interaction. This will check the relative block for each acceptible direction, and pass the
* resulting block face (if any) to the bounce pad. See {@link BouncyPad#bouncePad(Player, org.bukkit.block.BlockFace)}
* for how the resulting block face is processed.
*
* @param event The event which gets called when a player moves.
*/
@EventHandler
public void onPlayerMove(final PlayerMoveEvent event)
{
final Player player = event.getPlayer();
if (!this.hasPad(player))
{
return;
}
final BouncyPad pad = this.getPad(player);
final Location location = player.getLocation();
final Block xNeg1 = getRelative(location, -1, 0, 0);
final Block xPos1 = getRelative(location, 1, 0, 0);
final Block zNeg1 = getRelative(location, 0, 0, -1);
final Block zPos1 = getRelative(location, 0, 0, 1);
final Block yNeg1 = getRelative(location, 0, -1, 0);
Stream.of(xNeg1, xPos1, zNeg1, zPos1, yNeg1)
.filter(this::isWool)
.map(block -> block.getFace(location.getBlock()))
.findFirst()
.ifPresent(face -> pad.bouncePad(player, face));
}
/**
* Gets the relative block at the given location.
*
* @param location The location to get the relative block from.
* @param x The x mod.
* @param y The y mod.
* @param z The z mod.
* @return The relative block.
*/
private Block getRelative(final Location location, final int x, final int y, final int z)
{
return location.getBlock().getRelative(x, y, z);
}
/**
* Checks if the given block is wool.
*
* @param block The block to check.
* @return True if the block is wool, false otherwise.
* @see Tag#WOOL
*/
private boolean isWool(final Block block)
{
return Tag.WOOL.isTagged(block.getType());
}
}

View File

@ -0,0 +1,32 @@
package me.totalfreedom.fossil.bouncypads;
import org.bukkit.block.BlockFace;
/**
* Represents a specific type of bouncy pad.
*/
public enum PadType
{
/**
* A normal pad, which will only bounce the player if the face is {@link BlockFace#UP}.
*/
NORMAL,
/**
* A pad which will bounce the player on {@link BlockFace#NORTH}, {@link BlockFace#SOUTH}, {@link BlockFace#EAST}
* or {@link BlockFace#WEST}.
*/
SIDES,
/**
* A pad which will bounce the player if the face is {@link BlockFace#UP}, {@link BlockFace#NORTH},
* {@link BlockFace#EAST}, {@link BlockFace#SOUTH} or {@link BlockFace#WEST}.
*/
ALL,
/**
* A pad which will bounce the player with an extreme velocity
*/
EXTREME,
/**
* A pad which will send the player to space.
*/
SPACE_CADET;
}

View File

@ -0,0 +1,58 @@
package me.totalfreedom.fossil.reactions;
import me.totalfreedom.display.BossBarDisplay;
import me.totalfreedom.economy.EconomicEntity;
import me.totalfreedom.shop.Reaction;
import me.totalfreedom.shop.ReactionType;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.bossbar.BossBar;
import java.util.SplittableRandom;
import java.util.function.Consumer;
/**
* Represents a single chat reaction that can be performed by a player.
*/
public final class CopyCatReaction extends Reaction
{
private final long reward;
public CopyCatReaction(final long reward)
{
super(ReactionType.COPYCAT);
this.reward = reward;
}
@Override
public long getReward()
{
return reward;
}
@Override
public void onReact(final Consumer<EconomicEntity> entity)
{
entity.accept(null);
}
@Override
public void display(final Audience audience)
{
final BossBar bossBar = BossBarDisplay.builder().setName(getRandomCharacterString())
.setProgress(0.0F)
.build();
}
public String getRandomCharacterString() {
final SplittableRandom random = new SplittableRandom();
final StringBuilder sb = new StringBuilder(10);
final String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
for (int i = 0; i < 10; i++) {
sb.append(chars.charAt(random.nextInt(chars.length())));
}
return sb.toString();
}
}