diff --git a/eternalcombat-api/src/main/java/com/eternalcode/combat/EternalCombatApi.java b/eternalcombat-api/src/main/java/com/eternalcode/combat/EternalCombatApi.java index a45af55d..55e1fbc5 100644 --- a/eternalcombat-api/src/main/java/com/eternalcode/combat/EternalCombatApi.java +++ b/eternalcombat-api/src/main/java/com/eternalcode/combat/EternalCombatApi.java @@ -4,7 +4,7 @@ import com.eternalcode.combat.fight.FightManager; import com.eternalcode.combat.fight.drop.DropService; import com.eternalcode.combat.fight.effect.FightEffectService; -import com.eternalcode.combat.fight.pearl.FightPearlService; +import com.eternalcode.combat.fight.pearl.PearlService; import com.eternalcode.combat.region.RegionProvider; import com.eternalcode.combat.fight.tagout.FightTagOutService; @@ -14,7 +14,7 @@ public interface EternalCombatApi { RegionProvider getRegionProvider(); - FightPearlService getFightPearlService(); + PearlService getFightPearlService(); FightTagOutService getFightTagOutService(); diff --git a/eternalcombat-api/src/main/java/com/eternalcode/combat/fight/event/CauseOfTag.java b/eternalcombat-api/src/main/java/com/eternalcode/combat/fight/event/CauseOfTag.java index 32ec7eaa..9cbc001e 100644 --- a/eternalcombat-api/src/main/java/com/eternalcode/combat/fight/event/CauseOfTag.java +++ b/eternalcombat-api/src/main/java/com/eternalcode/combat/fight/event/CauseOfTag.java @@ -23,6 +23,17 @@ public enum CauseOfTag { */ CRYSTAL, + /** + * Trident usage extending combat tag. + */ + TRIDENT, + + /** + * Ender pearl usage extending combat tag. + */ + ENDER_PEARL, + + /** * A custom cause, typically defined by external plugins or systems, applied the combat tag. */ diff --git a/eternalcombat-api/src/main/java/com/eternalcode/combat/fight/pearl/FightPearlService.java b/eternalcombat-api/src/main/java/com/eternalcode/combat/fight/pearl/PearlService.java similarity index 54% rename from eternalcombat-api/src/main/java/com/eternalcode/combat/fight/pearl/FightPearlService.java rename to eternalcombat-api/src/main/java/com/eternalcode/combat/fight/pearl/PearlService.java index 18d2b84f..f0f29cd2 100644 --- a/eternalcombat-api/src/main/java/com/eternalcode/combat/fight/pearl/FightPearlService.java +++ b/eternalcombat-api/src/main/java/com/eternalcode/combat/fight/pearl/PearlService.java @@ -1,16 +1,16 @@ package com.eternalcode.combat.fight.pearl; import java.time.Duration; -import java.time.Instant; import java.util.UUID; +import org.bukkit.entity.Player; -public interface FightPearlService { +public interface PearlService { - Instant getDelay(UUID uuid); + boolean shouldCancelEvent(UUID playerId); Duration getRemainingDelay(UUID uuid); boolean hasDelay(UUID uuid); - void markDelay(UUID uuid); + void handleDelay(Player uuid); } diff --git a/eternalcombat-api/src/main/java/com/eternalcode/combat/fight/trident/TridentService.java b/eternalcombat-api/src/main/java/com/eternalcode/combat/fight/trident/TridentService.java new file mode 100644 index 00000000..8f87187e --- /dev/null +++ b/eternalcombat-api/src/main/java/com/eternalcode/combat/fight/trident/TridentService.java @@ -0,0 +1,35 @@ +package com.eternalcode.combat.fight.trident; + +import java.time.Duration; +import java.util.UUID; +import org.bukkit.entity.Player; + +public interface TridentService { + + /** + * returns remaining delay for the player to use trident again + * @param uuid unique id of the player + * @return remaining duration left to use trident again by the player or zero + */ + Duration getRemainingDelay(UUID uuid); + + /** + * checks if player still has delay left to use trident + * @param uuid unique id of the player + * @return true if user still has cooldown left to use trident + */ + boolean hasDelay(UUID uuid); + + /** + * marks start of the delay for the user + * @param uuid unique id of the player + */ + void markDelay(UUID uuid); + + /** + * handles the trident cooldown for the player, should be called when player uses riptide in combat + * @param player the player who used riptide in combat needed to apply cooldown to item + */ + void handleTridentDelay(Player player); + +} diff --git a/eternalcombat-plugin/build.gradle.kts b/eternalcombat-plugin/build.gradle.kts index fcd7c2ad..293582b6 100644 --- a/eternalcombat-plugin/build.gradle.kts +++ b/eternalcombat-plugin/build.gradle.kts @@ -1,6 +1,6 @@ -import net.minecrell.pluginyml.bukkit.BukkitPluginDescription + import io.papermc.hangarpublishplugin.model.Platforms -import org.gradle.kotlin.dsl.shadowJar +import net.minecrell.pluginyml.bukkit.BukkitPluginDescription plugins { `eternalcombat-java` @@ -93,7 +93,7 @@ bukkit { tasks { runServer { - minecraftVersion("1.21.10") + minecraftVersion("1.21.11") downloadPlugins.modrinth("WorldEdit", Versions.WORLDEDIT) downloadPlugins.modrinth("PacketEvents", "${Versions.PACKETEVENTS}+spigot") downloadPlugins.modrinth("WorldGuard", Versions.WORLDGUARD) diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java index 8df578c9..65b324df 100644 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java @@ -18,7 +18,10 @@ import com.eternalcode.combat.fight.firework.FireworkController; import com.eternalcode.combat.fight.knockback.KnockbackService; import com.eternalcode.combat.fight.tagout.FightTagOutService; -import com.eternalcode.combat.fight.pearl.FightPearlService; +import com.eternalcode.combat.fight.pearl.PearlService; +import com.eternalcode.combat.fight.trident.TridentController; +import com.eternalcode.combat.fight.trident.TridentService; +import com.eternalcode.combat.fight.trident.TridentServiceImpl; import com.eternalcode.combat.handler.InvalidUsageHandlerImpl; import com.eternalcode.combat.handler.MissingPermissionHandlerImpl; import com.eternalcode.combat.config.ConfigService; @@ -40,8 +43,8 @@ import com.eternalcode.combat.fight.effect.FightEffectServiceImpl; import com.eternalcode.combat.fight.logout.LogoutController; import com.eternalcode.combat.fight.logout.LogoutService; -import com.eternalcode.combat.fight.pearl.FightPearlController; -import com.eternalcode.combat.fight.pearl.FightPearlServiceImpl; +import com.eternalcode.combat.fight.pearl.PearlController; +import com.eternalcode.combat.fight.pearl.PearlServiceImpl; import com.eternalcode.combat.fight.tagout.FightTagOutController; import com.eternalcode.combat.fight.tagout.FightTagOutServiceImpl; import com.eternalcode.combat.fight.tagout.FightTagOutCommand; @@ -80,7 +83,8 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi { private static final int BSTATS_METRICS_ID = 17803; private FightManager fightManager; - private FightPearlService fightPearlService; + private PearlService pearlService; + private TridentService tridentService; private FightTagOutService fightTagOutService; private FightEffectService fightEffectService; @@ -108,7 +112,8 @@ public void onEnable() { MinecraftScheduler scheduler = CombatSchedulerAdapter.getAdaptiveScheduler(this); this.fightManager = new FightManagerImpl(eventManager); - this.fightPearlService = new FightPearlServiceImpl(pluginConfig.pearl); + this.pearlService = new PearlServiceImpl(this.fightManager, pluginConfig, scheduler); + this.tridentService = new TridentServiceImpl(this.fightManager, pluginConfig); this.fightTagOutService = new FightTagOutServiceImpl(); this.fightEffectService = new FightEffectServiceImpl(); @@ -179,7 +184,8 @@ public void onEnable() { new FightBypassPermissionController(server, pluginConfig), new FightBypassCreativeController(server, pluginConfig), new FightActionBlockerController(this.fightManager, noticeService, pluginConfig, server), - new FightPearlController(pluginConfig.pearl, noticeService, this.fightManager, this.fightPearlService), + new PearlController(pluginConfig, this.pearlService, noticeService), + new TridentController(pluginConfig, noticeService, this.fightManager, this.tridentService), new UpdaterNotificationController(updaterService, pluginConfig, this.audienceProvider, miniMessage), new KnockbackRegionController(noticeService, this.regionProvider, this.fightManager, knockbackService, server), new FightEffectController(pluginConfig.effect, this.fightEffectService, this.fightManager, server), @@ -237,8 +243,8 @@ public RegionProvider getRegionProvider() { } @Override - public FightPearlService getFightPearlService() { - return this.fightPearlService; + public PearlService getFightPearlService() { + return this.pearlService; } @Override diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/PluginConfig.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/PluginConfig.java index ac46f959..8d557a24 100644 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/PluginConfig.java +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/PluginConfig.java @@ -4,7 +4,8 @@ import com.eternalcode.combat.fight.drop.DropSettings; import com.eternalcode.combat.fight.effect.FightEffectSettings; import com.eternalcode.combat.fight.knockback.KnockbackSettings; -import com.eternalcode.combat.fight.pearl.FightPearlSettings; +import com.eternalcode.combat.fight.pearl.PearlSettings; +import com.eternalcode.combat.fight.trident.TridentSettings; import eu.okaeri.configs.OkaeriConfig; import eu.okaeri.configs.annotation.Comment; import java.time.Duration; @@ -33,7 +34,14 @@ public class PluginConfig extends OkaeriConfig { "# Settings related to Ender Pearls.", "# Configure cooldowns, restrictions, and other behaviors for Ender Pearls during combat." }) - public FightPearlSettings pearl = new FightPearlSettings(); + public PearlSettings pearl = new PearlSettings(); + + @Comment({ + " ", + "# Settings related to Trident", + "# Configure cooldowns, restrictions, and other behaviors for Trident during combat." + }) + public TridentSettings trident = new TridentSettings(); @Comment({ " ", diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/FightPearlController.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/FightPearlController.java deleted file mode 100644 index bfd35700..00000000 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/FightPearlController.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.eternalcode.combat.fight.pearl; - -import com.eternalcode.combat.fight.FightManager; -import com.eternalcode.combat.notification.NoticeService; -import com.eternalcode.combat.util.DurationUtil; -import java.time.Duration; -import java.util.UUID; -import org.bukkit.Material; -import org.bukkit.entity.EnderPearl; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.event.entity.EntityDamageEvent; -import org.bukkit.event.entity.ProjectileLaunchEvent; -import org.bukkit.inventory.ItemStack; - -public class FightPearlController implements Listener { - - private final FightPearlSettings settings; - private final NoticeService noticeService; - private final FightManager fightManager; - private final FightPearlService fightPearlService; - - public FightPearlController( - FightPearlSettings settings, - NoticeService noticeService, - FightManager fightManager, - FightPearlService fightPearlService - ) { - this.settings = settings; - this.noticeService = noticeService; - this.fightManager = fightManager; - this.fightPearlService = fightPearlService; - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void onPearlThrow(ProjectileLaunchEvent event) { - if (!(event.getEntity() instanceof EnderPearl)) { - return; - } - - if (!(event.getEntity().getShooter() instanceof Player player)) { - return; - } - - UUID playerId = player.getUniqueId(); - - if (!this.fightManager.isInCombat(playerId)) { - return; - } - - if (this.settings.pearlThrowDisabledDuringCombat) { - event.setCancelled(true); - this.noticeService.create() - .player(playerId) - .notice(this.settings.pearlThrowBlockedDuringCombat) - .send(); - return; - } - - if (this.settings.pearlCooldownEnabled) { - handlePearlCooldown(event, player, playerId); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onPearlDamage(EntityDamageByEntityEvent event) { - if (this.settings.pearlThrowDamageEnabled) { - return; - } - - if (!(event.getEntity() instanceof Player) || - !(event.getDamager() instanceof EnderPearl) || - event.getCause() != EntityDamageEvent.DamageCause.FALL) { - return; - } - - event.setDamage(0.0); - } - - private void handlePearlCooldown(ProjectileLaunchEvent event, Player player, UUID playerId) { - if (this.settings.pearlThrowDelay.isZero()) { - return; - } - - if (this.fightPearlService.hasDelay(playerId)) { - event.setCancelled(true); - Duration remainingDelay = this.fightPearlService.getRemainingDelay(playerId); - - this.noticeService.create() - .player(playerId) - .notice(this.settings.pearlThrowBlockedDelayDuringCombat) - .placeholder("{TIME}", DurationUtil.format(remainingDelay)) - .send(); - return; - } - - this.fightPearlService.markDelay(playerId); - int cooldownTicks = (int) (this.settings.pearlThrowDelay.toMillis() / 50); - player.setCooldown(Material.ENDER_PEARL, cooldownTicks); - } -} diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/FightPearlServiceImpl.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/FightPearlServiceImpl.java deleted file mode 100644 index 584d275d..00000000 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/FightPearlServiceImpl.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.eternalcode.combat.fight.pearl; - -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; - -import java.time.Duration; -import java.time.Instant; -import java.util.UUID; - -public class FightPearlServiceImpl implements FightPearlService { - - private final FightPearlSettings pearlSettings; - private final Cache pearlStartTimes; - - public FightPearlServiceImpl(FightPearlSettings pearlSettings) { - this.pearlSettings = pearlSettings; - this.pearlStartTimes = Caffeine.newBuilder() - .expireAfterWrite(pearlSettings.pearlThrowDelay) - .build(); - } - - @Override - public void markDelay(UUID uuid) { - this.pearlStartTimes.put(uuid, Instant.now()); - } - - @Override - public boolean hasDelay(UUID uuid) { - return this.pearlStartTimes.getIfPresent(uuid) != null; - } - - @Override - public Duration getRemainingDelay(UUID uuid) { - Instant startTime = this.pearlStartTimes.getIfPresent(uuid); - if (startTime == null) { - return Duration.ZERO; - } - - Duration elapsed = Duration.between(startTime, Instant.now()); - Duration remaining = this.pearlSettings.pearlThrowDelay.minus(elapsed); - - return remaining.isNegative() ? Duration.ZERO : remaining; - } - - @Override - public Instant getDelay(UUID uuid) { - Instant startTime = this.pearlStartTimes.getIfPresent(uuid); - return startTime != null ? startTime.plus(this.pearlSettings.pearlThrowDelay) : Instant.MIN; - } -} diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/PearlController.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/PearlController.java new file mode 100644 index 00000000..823e6c4b --- /dev/null +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/PearlController.java @@ -0,0 +1,82 @@ +package com.eternalcode.combat.fight.pearl; + +import com.eternalcode.combat.config.implementation.PluginConfig; +import com.eternalcode.combat.notification.NoticeService; +import com.eternalcode.combat.util.DurationUtil; +import java.time.Duration; +import java.util.UUID; +import org.bukkit.entity.EnderPearl; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.ProjectileLaunchEvent; + +public class PearlController implements Listener { + + private final PluginConfig pluginConfig; + private final PearlService pearlService; + private final NoticeService noticeService; + + public PearlController( + PluginConfig pluginConfig, + PearlService pearlService, NoticeService noticeService + ) { + this.pluginConfig = pluginConfig; + this.pearlService = pearlService; + this.noticeService = noticeService; + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPearlThrow(ProjectileLaunchEvent event) { + if (!(event.getEntity() instanceof EnderPearl)) { + return; + } + + if (!(event.getEntity().getShooter() instanceof Player player)) { + return; + } + + UUID playerId = player.getUniqueId(); + + if (this.pearlService.shouldCancelEvent(playerId)) { + event.setCancelled(true); + + if (this.pluginConfig.pearl.pearlThrowDisabledDuringCombat) { + this.noticeService.create() + .player(playerId) + .notice(this.pluginConfig.pearl.pearlThrowBlockedDuringCombat) + .send(); + return; + } + + Duration remainingDelay = this.pearlService.getRemainingDelay(playerId); + this.noticeService.create() + .player(playerId) + .notice(this.pluginConfig.pearl.pearlThrowBlockedDelayDuringCombat) + .placeholder("{TIME}", DurationUtil.format(remainingDelay)) + .send(); + + } + + this.pearlService.handleDelay(player); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPearlDamage(EntityDamageByEntityEvent event) { + if (this.pluginConfig.pearl.pearlThrowDamageEnabled) { + return; + } + + if (!(event.getEntity() instanceof Player) || + !(event.getDamager() instanceof EnderPearl) || + event.getCause() != EntityDamageEvent.DamageCause.PROJECTILE) { + return; + } + + event.setDamage(0.0); + } + +} diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/PearlServiceImpl.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/PearlServiceImpl.java new file mode 100644 index 00000000..2f5d1943 --- /dev/null +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/PearlServiceImpl.java @@ -0,0 +1,81 @@ +package com.eternalcode.combat.fight.pearl; + +import com.eternalcode.combat.config.implementation.PluginConfig; +import com.eternalcode.combat.fight.FightManager; +import com.eternalcode.combat.fight.event.CauseOfTag; +import com.eternalcode.combat.util.delay.Delay; + +import com.eternalcode.commons.scheduler.Scheduler; +import java.time.Duration; +import java.util.UUID; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class PearlServiceImpl implements PearlService { + + private final FightManager fightManager; + private final PluginConfig pluginConfig; + private final Scheduler scheduler; + + private final Delay pearlStartTimes; + + public PearlServiceImpl(FightManager fightManager, PluginConfig pluginConfig, Scheduler scheduler) { + this.fightManager = fightManager; + this.pluginConfig = pluginConfig; + + this.pearlStartTimes = Delay.withDefault(() -> pluginConfig.pearl.pearlThrowDelay); + this.scheduler = scheduler; + } + + @Override + public boolean shouldCancelEvent(UUID playerId) { + if (fightManager.isInCombat(playerId)) { + if (this.pluginConfig.pearl.pearlCooldownEnabled) { + return this.pearlStartTimes.hasDelay(playerId); + } + + return pluginConfig.pearl.pearlThrowDisabledDuringCombat; + } + + return false; + } + + @Override + public void handleDelay(Player player) { + UUID uniqueId = player.getUniqueId(); + + if (this.hasDelay(uniqueId)) { + return; + } + + if (this.fightManager.isInCombat(uniqueId)) { + if (this.pluginConfig.pearl.pearlResetsTimer) { + Duration combatTime = this.pluginConfig.settings.combatTimerDuration; + this.fightManager.tag(uniqueId, combatTime, CauseOfTag.ENDER_PEARL); + } + + if (this.pluginConfig.pearl.pearlCooldownEnabled && !this.pluginConfig.pearl.pearlThrowDelay.isZero()) { + this.pearlStartTimes.markDelay(uniqueId); + this.scheduler.runLater( + () -> player.setCooldown( + Material.ENDER_PEARL, + (int) this.pluginConfig.pearl.pearlThrowDelay.toMillis() / 50 + ), Duration.ofMillis(50) + ); + + } + + } + } + + @Override + public boolean hasDelay(UUID uuid) { + return this.pearlStartTimes.hasDelay(uuid); + } + + @Override + public Duration getRemainingDelay(UUID uuid) { + return this.pearlStartTimes.getRemaining(uuid); + } + +} diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/FightPearlSettings.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/PearlSettings.java similarity index 89% rename from eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/FightPearlSettings.java rename to eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/PearlSettings.java index 4cb29803..0c25c2e7 100644 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/FightPearlSettings.java +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/pearl/PearlSettings.java @@ -7,7 +7,7 @@ import java.time.Duration; -public class FightPearlSettings extends OkaeriConfig { +public class PearlSettings extends OkaeriConfig { @Comment({ "# Is pearl damage to be enabled?", "# This will work globally" }) public boolean pearlThrowDamageEnabled = true; @@ -21,6 +21,9 @@ public class FightPearlSettings extends OkaeriConfig { @Comment("# Set true, If you want add cooldown to pearls") public boolean pearlCooldownEnabled = false; + @Comment("# Set true, If you want to reset timer when player throws ender pearl") + public boolean pearlResetsTimer = true; + @Comment({ "# Block throwing pearls with delay?", "# If you set this to for example 3s, player will have to wait 3 seconds before throwing another pearl" diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/trident/TridentController.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/trident/TridentController.java new file mode 100644 index 00000000..18ca0eb6 --- /dev/null +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/trident/TridentController.java @@ -0,0 +1,98 @@ +package com.eternalcode.combat.fight.trident; + +import com.eternalcode.combat.config.implementation.PluginConfig; +import com.eternalcode.combat.fight.FightManager; +import com.eternalcode.combat.fight.event.CauseOfTag; +import com.eternalcode.combat.notification.NoticeService; +import com.eternalcode.combat.util.DurationUtil; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerRiptideEvent; + +import java.time.Duration; +import java.util.UUID; + +public class TridentController implements Listener { + + private final PluginConfig pluginConfig; + private final NoticeService noticeService; + private final FightManager fightManager; + private final TridentService tridentService; + + public TridentController( + PluginConfig pluginConfig, + NoticeService noticeService, + FightManager fightManager, + TridentService tridentService + ) { + this.pluginConfig = pluginConfig; + this.noticeService = noticeService; + this.fightManager = fightManager; + this.tridentService = tridentService; + } + + @EventHandler + public void onRiptide(PlayerRiptideEvent event) { + Player player = event.getPlayer(); + UUID uniqueId = player.getUniqueId(); + + if (!this.fightManager.isInCombat(uniqueId)) { + return; + } + + if (this.pluginConfig.trident.tridentRiptideDisabledDuringCombat) { + return; + } + + + if (this.tridentService.hasDelay(uniqueId)) { + Duration remainingDelay = this.tridentService.getRemainingDelay(uniqueId); + + this.noticeService.create() + .player(uniqueId) + .notice(this.pluginConfig.trident.tridentRiptideOnCooldown) + .placeholder("{TIME}", DurationUtil.format(remainingDelay)) + .send(); + + return; + } + + this.tridentService.handleTridentDelay(player); + + if (this.pluginConfig.trident.riptideResetsTimerEnabled) { + this.fightManager.tag(uniqueId, this.pluginConfig.settings.combatTimerDuration, CauseOfTag.TRIDENT); + } + } + + @EventHandler(ignoreCancelled = true) + public void onPlayerMove(PlayerMoveEvent event) { + if (event.getTo() == null) { + return; + } + + if (event.getFrom().distanceSquared(event.getTo()) == 0) { + return; + } + + Player player = event.getPlayer(); + UUID playerId = player.getUniqueId(); + + if (!player.isRiptiding()) { + return; + } + + if (!this.fightManager.isInCombat(playerId)) { + return; + } + + if (this.pluginConfig.trident.tridentRiptideDisabledDuringCombat) { + event.setCancelled(true); + this.noticeService.create() + .player(playerId) + .notice(this.pluginConfig.trident.tridentRiptideBlocked) + .send(); + } + } +} diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/trident/TridentServiceImpl.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/trident/TridentServiceImpl.java new file mode 100644 index 00000000..aa143480 --- /dev/null +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/trident/TridentServiceImpl.java @@ -0,0 +1,62 @@ +package com.eternalcode.combat.fight.trident; + +import com.eternalcode.combat.config.implementation.PluginConfig; +import com.eternalcode.combat.fight.FightManager; +import com.eternalcode.combat.util.delay.Delay; + +import java.time.Duration; +import java.util.UUID; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class TridentServiceImpl implements TridentService { + + private final FightManager fightManager; + private final PluginConfig pluginConfig; + + private final Delay delay; + + public TridentServiceImpl(FightManager fightManager, PluginConfig pluginConfig) { + this.fightManager = fightManager; + this.pluginConfig = pluginConfig; + + this.delay = Delay.withDefault(() -> pluginConfig.trident.tridentRiptideDelay); + } + + @Override + public void handleTridentDelay(Player player) { + UUID uniqueId = player.getUniqueId(); + + if (!this.fightManager.isInCombat(uniqueId)) { + return; + } + + if (this.hasDelay(uniqueId)) { + return; + } + + if (this.pluginConfig.trident.tridentRiptideDelay.isZero()) { + return; + } + + this.markDelay(uniqueId); + player.setCooldown(Material.TRIDENT, (int) this.pluginConfig.trident.tridentRiptideDelay.toMillis() / 50); + + } + + @Override + public void markDelay(UUID uuid) { + this.delay.markDelay(uuid); + } + + @Override + public boolean hasDelay(UUID uuid) { + return this.delay.hasDelay(uuid); + } + + @Override + public Duration getRemainingDelay(UUID uuid) { + return this.delay.getRemaining(uuid); + } +} + diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/trident/TridentSettings.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/trident/TridentSettings.java new file mode 100644 index 00000000..d496cac0 --- /dev/null +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/trident/TridentSettings.java @@ -0,0 +1,40 @@ +package com.eternalcode.combat.fight.trident; + +import com.eternalcode.multification.bukkit.notice.BukkitNotice; +import com.eternalcode.multification.notice.Notice; +import eu.okaeri.configs.OkaeriConfig; +import eu.okaeri.configs.annotation.Comment; + +import java.time.Duration; + +public class TridentSettings extends OkaeriConfig { + + @Comment({ + "# Set to true to disable riptide usage during combat", + "# This setting works globally, but can be overridden by region settings" + }) + public boolean tridentRiptideDisabledDuringCombat = false; + + @Comment("# Set to true so the users will get combat log when they use riptide") + public boolean riptideResetsTimerEnabled = false; + + @Comment({ + "# Should riptide enchantment be on cooldown?", + "# Setting this option to 3s will make players wait 3 seconds between trident throws", + "# Setting this to 0s will remove cooldown" + }) + public Duration tridentRiptideDelay = Duration.ofSeconds(10); + + @Comment("# Message shown when riptide is blocked during combat") + public Notice tridentRiptideBlocked = BukkitNotice.builder() + .chat("Using riptide is prohibited during combat!") + .build(); + + @Comment({ + "# Message sent to the player when riptide is on cooldown", + "# Available placeholder: {TIME} - remaining time left to use riptide again" + }) + public Notice tridentRiptideOnCooldown = BukkitNotice.builder() + .chat("You must wait {TIME} before next riptide!") + .build(); +} diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/util/delay/Delay.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/util/delay/Delay.java new file mode 100644 index 00000000..b7544fdd --- /dev/null +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/util/delay/Delay.java @@ -0,0 +1,58 @@ +package com.eternalcode.combat.util.delay; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import java.time.Duration; +import java.time.Instant; +import java.util.function.Supplier; + +public class Delay { + + private final Cache cache; + private final Supplier defaultDelay; + + private Delay(Supplier defaultDelay) { + if (defaultDelay == null) { + throw new IllegalArgumentException("defaultDelay cannot be null"); + } + + this.defaultDelay = defaultDelay; + this.cache = Caffeine.newBuilder() + .expireAfter(new InstantExpiry()) + .build(); + } + + public void markDelay(T key, Duration delay) { + if (delay.isZero() || delay.isNegative()) { + this.cache.invalidate(key); + } + + this.cache.put(key, Instant.now().plus(delay)); + } + + public void markDelay(T key) { + this.markDelay(key, this.defaultDelay.get()); + } + + public void unmarkDelay(T key) { + this.cache.invalidate(key); + } + + public boolean hasDelay(T key) { + Instant delayExpireMoment = this.getExpireAt(key); + return Instant.now().isBefore(delayExpireMoment); + } + + public Duration getRemaining(T key) { + return Duration.between(Instant.now(), this.getExpireAt(key)); + } + + private Instant getExpireAt(T key) { + return this.cache.asMap().getOrDefault(key, Instant.MIN); + } + + public static Delay withDefault(Supplier defaultDelay) { + return new Delay<>(defaultDelay); + } + +} diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/util/delay/InstantExpiry.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/util/delay/InstantExpiry.java new file mode 100644 index 00000000..ccd213dd --- /dev/null +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/util/delay/InstantExpiry.java @@ -0,0 +1,39 @@ +package com.eternalcode.combat.util.delay; + +import com.github.benmanes.caffeine.cache.Expiry; +import java.time.Duration; +import java.time.Instant; +import org.jetbrains.annotations.NotNull; + +class InstantExpiry implements Expiry<@NotNull T, @NotNull Instant> { + + @Override + public long expireAfterCreate(@NotNull T key, @NotNull Instant expireTime, long currentTime) { + return timeToExpire(expireTime); + } + + @Override + public long expireAfterUpdate(@NotNull T key, @NotNull Instant newExpireTime, long currentTime, long currentDuration) { + return timeToExpire(newExpireTime); + } + + @Override + public long expireAfterRead(@NotNull T key, @NotNull Instant value, long currentTime, long currentDuration) { + return currentDuration; + } + + private static long timeToExpire(Instant expireTime) { + Duration toExpire = Duration.between(Instant.now(), expireTime); + if (toExpire.isNegative()) { + return 0; + } + + long nanos = toExpire.toNanos(); + if (nanos == 0) { + return 1; + } + + return nanos; + } + +}