Skip to content

Commit

Permalink
Add interactions which have to be held for a period of time
Browse files Browse the repository at this point in the history
  • Loading branch information
tgross03 committed Nov 3, 2024
1 parent cfb274c commit 8f11fd1
Show file tree
Hide file tree
Showing 7 changed files with 401 additions and 8 deletions.
9 changes: 6 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>dev.edgetom</groupId>
<artifactId>interaction-api</artifactId>
<version>0.1.4</version>
<version>0.1.5</version>

<!-- Parts of this file were taken and modified from https://github.com/SkytAsul/GlowingEntities/blob/master/pom.xml -->

Expand Down Expand Up @@ -36,8 +36,7 @@
</scm>

<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<lombok.version>1.18.34</lombok.version>
</properties>
Expand All @@ -48,6 +47,10 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package dev.edgetom.interactions;

import lombok.Getter;
import lombok.Setter;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.block.Action;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;

/**
* A different variant of an {@link InteractionExecutor} which can be used if an interaction
* requires players to <strong>hold</strong> an interaction with a specific item (e.g. press RIGHT CLICK with
* an item for 2 seconds (40 ticks)).
* <p>
* Internally it is not possible to check if someone actually holds a button. Instead, the system checks if the
* server registers interactions within a predefined time (or tick) margin. If for example the server registers a
* click with the specified item every 5 ticks, it will count it as if the interaction is being held continuously.
* The time margin can be changed by calling {@link InteractionManager#setHoldDownEventTriggerTicks(long)} or in the
* constructor of the {@link InteractionManager}.
*/

@Setter
@Getter
@SuppressWarnings("unused")
public abstract class HoldDownInteractionExecutor extends InteractionExecutor {

/**
* The amount of ticks the interaction has to be held continuously to be executed.
*/
protected long holdDownDuration;

/**
* Create an {@link InteractionExecutor}.
*
* @param interactionManager The manager defined in the plugins main class controlling the interactions.
* @param interactionKey The key to be put into the {@link org.bukkit.persistence.PersistentDataContainer}
* of the item to interact with.
* @param holdDownDuration The amount of ticks the interaction has to be held continuously to be executed.
* @param placeable Whether the {@link ItemStack} the player interacts with should be able to be placed.
* Also affects whether block interactions (e.g. pressing buttons is possible with the item.
* @param cooldown The ticks that the interaction should be on cooldown after the interaction. Set {@code 0} for no cooldown.
* @param cooldownInstant Whether the cooldown should be applied immediately after the interaction triggers.
* If set to false the cooldown has to applied by using the {@link #addCooldown(Player, Material)} method
* @param actions The {@link Action}s that should trigger the executor.
*/
public HoldDownInteractionExecutor(@NotNull InteractionManager interactionManager, @NotNull String interactionKey, long holdDownDuration, boolean placeable, int cooldown, boolean cooldownInstant, Action... actions) {
super(interactionManager, interactionKey, placeable, cooldown, cooldownInstant, actions);
this.holdDownDuration = holdDownDuration;
}

/**
* Create an {@link HoldDownInteractionExecutor}.
*
* @param interactionManager The manager defined in the plugins main class controlling the interactions.
* @param interactionKey The {@link String} to be put into the {@link org.bukkit.persistence.PersistentDataContainer}
* of the item to interact with.
* @param holdDownDuration The amount of ticks the interaction has to be held continuously to be executed.
* @param placeable Whether the {@link ItemStack} the player interacts with should be able to be placed.
* Also affects whether block interactions (e.g. pressing buttons) is possible with the item.
* @param cooldown The ticks that the interaction should be on cooldown after the interaction. Set {@code 0} for no cooldown.
* @param cooldownInstant Whether the cooldown should be applied immediately after the interaction triggers.
* If set to false the cooldown has to applied by using the {@link #addCooldown(Player, Material)} method
* @param actionClass The {@link ActionClass} that categorizes the {@link Action}s which should trigger the executor.
*/
public HoldDownInteractionExecutor(@NotNull InteractionManager interactionManager, @NotNull String interactionKey, long holdDownDuration, boolean placeable, int cooldown, boolean cooldownInstant, ActionClass actionClass) {
super(interactionManager, interactionKey, placeable, cooldown, cooldownInstant, actionClass);
this.holdDownDuration = holdDownDuration;
}

/**
* Create an {@link InteractionExecutor} <strong>without a cooldown</strong>.
*
* @param interactionManager The manager defined in the plugins main class controlling the interactions.
* @param interactionKey The {@link String} to be put into the {@link org.bukkit.persistence.PersistentDataContainer}
* of the item to interact with.
* @param holdDownDuration The amount of ticks the interaction has to be held continuously to be executed.
* @param placeable Whether the {@link ItemStack} the player interacts with should be able to be placed.
* Also affects whether block interactions (e.g. pressing buttons) is possible with the item.
* If set to false the cooldown has to applied by using the {@link #addCooldown(Player, Material)} method
* @param actions The {@link Action}s that should trigger the executor.
*/
public HoldDownInteractionExecutor(@NotNull InteractionManager interactionManager, @NotNull String interactionKey, long holdDownDuration, boolean placeable, Action... actions) {
super(interactionManager, interactionKey, placeable, actions);
this.holdDownDuration = holdDownDuration;
}

/**
* Create an {@link InteractionExecutor} <strong>without a cooldown</strong>.
*
* @param interactionManager The manager defined in the plugins main class controlling the interactions.
* @param interactionKey The {@link String} to be put into the {@link org.bukkit.persistence.PersistentDataContainer}
* of the item to interact with.
* @param holdDownDuration The amount of ticks the interaction has to be held continuously to be executed.
* @param placeable Whether the {@link ItemStack} the player interacts with should be able to be placed.
* Also affects whether block interactions (e.g. pressing buttons) is possible with the item.
* If set to false the cooldown has to applied by using the {@link #addCooldown(Player, Material)} method
* @param actionClass The {@link ActionClass} that categorizes the {@link Action}s which should trigger the executor.
*/
public HoldDownInteractionExecutor(@NotNull InteractionManager interactionManager, @NotNull String interactionKey, long holdDownDuration, boolean placeable, ActionClass actionClass) {
super(interactionManager, interactionKey, placeable, actionClass);
this.holdDownDuration = holdDownDuration;
}

/**
* The method which is called every tick which passes after the interaction is triggered for the first time
* until the interaction is finished or canceled.
*
* @param player The player who caused the interaction.
* @param tickSinceFirstClick The ticks which elapsed since the interaction was triggered for the first time.
* @param ticksSinceLastClick The ticks which elapsed since the last click was registered.
*/
public abstract void onTickCheck(Player player, long tickSinceFirstClick, long ticksSinceLastClick);

/**
* The method which is called if the interaction is canceled because the player stopped interacting with the item.
*
* @param player The player who caused the interaction.
* @param tickSinceFirstClick The ticks which elapsed since the interaction was triggered for the first time.
* @param ticksSinceLastClick The ticks which elapsed since the last click was registered.
*/
public abstract void onCancel(Player player, long tickSinceFirstClick, long ticksSinceLastClick);

}
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,15 @@ public InteractionExecutor(@NotNull InteractionManager interactionManager, @NotN
*/
public abstract void execute(PlayerInteractEvent event, Player player);

/**
* An optional method which can be overwritten to react to the interaction being called while on cooldown (not implemented by default).
*
* @param event The triggered {@link PlayerInteractEvent}.
* @param player The player responsible for the event.
*/
public void interactedOnCooldown(PlayerInteractEvent event, Player player) {
}

/**
* Adds the {@link #interactionKey} to the {@link org.bukkit.persistence.PersistentDataContainer}
* of the given item.
Expand Down
33 changes: 29 additions & 4 deletions src/main/lombok/dev/edgetom/interactions/InteractionListener.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.edgetom.interactions;

import dev.edgetom.interactions.utils.HoldDownInteraction;
import lombok.AllArgsConstructor;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
Expand Down Expand Up @@ -50,14 +51,38 @@ public void onPlayerInteract(PlayerInteractEvent event) {
return;
}

interactionExecutor.execute(event, event.getPlayer());
if (!interactionExecutor.isPlaceable())
event.setCancelled(true);

if (interactionExecutor instanceof HoldDownInteractionExecutor holdDownInteractionExecutor) {

HoldDownInteraction holdDownInteraction = interactionManager.getHoldDownInteractions().get(event.getPlayer());

if (holdDownInteraction == null) {
new HoldDownInteraction(holdDownInteractionExecutor, event.getPlayer());

} else if (holdDownInteraction.getInteractionExecutor().equals(holdDownInteractionExecutor)) {

if (!holdDownInteraction.onClick()) {
new HoldDownInteraction(holdDownInteractionExecutor, event.getPlayer());
return;
}

if (holdDownInteraction.isFinished()) {
holdDownInteractionExecutor.execute(event, event.getPlayer());
if (holdDownInteractionExecutor.getCooldown() > 0 && holdDownInteractionExecutor.isCooldownInstant())
holdDownInteractionExecutor.addCooldown(event.getPlayer(), event.getItem().getType());
holdDownInteraction.cancel(false);
}

}

} else
interactionExecutor.execute(event, event.getPlayer());

if (interactionExecutor.getCooldown() > 0 && interactionExecutor.isCooldownInstant())
interactionExecutor.addCooldown(event.getPlayer(), event.getItem().getType());

if (!interactionExecutor.isPlaceable())
event.setCancelled(true);

}

}
50 changes: 49 additions & 1 deletion src/main/lombok/dev/edgetom/interactions/InteractionManager.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package dev.edgetom.interactions;

import dev.edgetom.interactions.utils.HoldDownInteraction;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;

import java.util.HashMap;
Expand All @@ -11,8 +14,15 @@
* The InteractionManager controls the assignment of the interactions that are
* called in the {@link InteractionExecutor}s.
*/
@SuppressWarnings("unused")
public class InteractionManager {

/**
* The plugin in which the manager is registered.
*/
@Getter
private final Plugin plugin;

/**
* The unique key which is used for the entries in the {@link org.bukkit.persistence.PersistentDataContainer}
* of items which have an InteractionExecutor. The key is generated automatically when initializing this manager.
Expand All @@ -21,18 +31,55 @@ public class InteractionManager {
private final NamespacedKey persistentDataContainerKey;

/**
* A {@link HashMap} containing the interactions
* A {@link HashMap} containing the InteractionExecutors
*/
private final HashMap<String, InteractionExecutor> interactions;


/**
* The maximal amount of ticks between two {@link org.bukkit.event.player.PlayerInteractEvent}
* triggers to be counted as holding an interaction button (default is {@code 5})
* Should be chosen higher than 5 if the sever which runs the plugin is not tick stable, meaning,
* the ticks between two event triggers change.
*/
@Getter
@Setter
private long holdDownEventTriggerTicks = 5;

/**
* A {@link HashMap} containing the HoldDownInteractions
*/
@Getter
private final HashMap<Player, HoldDownInteraction> holdDownInteractions;

/**
* Creates a new {@link InteractionManager}.
*
* @param plugin The plugin instance
*/
public InteractionManager(Plugin plugin) {
this.plugin = plugin;
this.persistentDataContainerKey = new NamespacedKey(plugin, UUID.randomUUID().toString().toLowerCase());
this.interactions = new HashMap<>();
this.holdDownInteractions = new HashMap<>();
plugin.getServer().getPluginManager().registerEvents(new InteractionListener(this), plugin);
}

/**
* Creates a new {@link InteractionManager}
*
* @param plugin The plugin instance
* @param holdDownEventTriggerTicks The maximal amount of ticks between two {@link org.bukkit.event.player.PlayerInteractEvent}
* triggers to be counted as holding an interaction button (default is {@code 5}).
* Should be chosen higher than 5 if the sever which runs the plugin is not tick stable, meaning,
* the ticks between two event triggers change.
*/
public InteractionManager(Plugin plugin, long holdDownEventTriggerTicks) {
this.plugin = plugin;
this.holdDownEventTriggerTicks = holdDownEventTriggerTicks;
this.persistentDataContainerKey = new NamespacedKey(plugin, UUID.randomUUID().toString().toLowerCase());
this.interactions = new HashMap<>();
this.holdDownInteractions = new HashMap<>();
plugin.getServer().getPluginManager().registerEvents(new InteractionListener(this), plugin);
}

Expand All @@ -59,6 +106,7 @@ public void registerInteraction(InteractionExecutor executor) {

/**
* Unregisters an {@link InteractionExecutor} so that it can't be called anymore.
*
* @param executor The executor to unregister.
*/
public void unregisterInteraction(InteractionExecutor executor) {
Expand Down
Loading

0 comments on commit 8f11fd1

Please sign in to comment.