From cd882a4c4d7abd7ec0e5ed3031d8844d013a0cf3 Mon Sep 17 00:00:00 2001 From: Ryan <7389646+ryanbarlow97@users.noreply.github.com> Date: Thu, 24 Sep 2026 16:43:25 +0000 Subject: [PATCH] fix: gate rune keybind casts by weapon attunement Right-click and shift-click rune abilities were ignored because the cast listener only watched CAST and API. Those keybinds are already listed in config, so the listener now treats every configured trigger as a cast. Co-authored-by: Cursor --- .../java/net/tfminecraft/magic/Cache.java | 24 +++++++++++++++++++ .../magic/integration/SkillIdResolver.java | 20 +++++++++++++++- .../magic/loader/ConfigLoader.java | 11 +++++++++ src/main/resources/config.yml | 12 +++++++++- 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/tfminecraft/magic/Cache.java b/src/main/java/net/tfminecraft/magic/Cache.java index 9878c65..3061678 100644 --- a/src/main/java/net/tfminecraft/magic/Cache.java +++ b/src/main/java/net/tfminecraft/magic/Cache.java @@ -1,5 +1,10 @@ package net.tfminecraft.magic; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Set; + /** * Runtime flags from config.yml. */ @@ -25,6 +30,11 @@ public final class Cache { public static double castDriftMin = 0.01; /** Seconds between repeat chat lines explaining why a weapon refused a spell. */ public static long refuseChatMillis = 30000L; + /** + * Extra MythicLib triggers treated as a cast, from {@code runes.keybinds}. + * Names are uppercase. {@code CAST} and {@code API} are always included in the check. + */ + public static volatile Set castTriggers = Set.of(); public static double tickIntervalSeconds() { return Math.max(1L, tickIntervalTicks) / 20.0; @@ -35,5 +45,19 @@ public static double tickHours() { return tickIntervalSeconds() / scale; } + /** Replaces the configured cast triggers. Blank entries are dropped. */ + public static void setCastTriggers(Iterable names) { + LinkedHashSet next = new LinkedHashSet<>(); + if (names != null) { + for (String name : names) { + if (name == null || name.isBlank()) { + continue; + } + next.add(name.trim().toUpperCase(Locale.ROOT)); + } + } + castTriggers = Collections.unmodifiableSet(next); + } + private Cache() {} } diff --git a/src/main/java/net/tfminecraft/magic/integration/SkillIdResolver.java b/src/main/java/net/tfminecraft/magic/integration/SkillIdResolver.java index 86b44b4..50c9e9e 100644 --- a/src/main/java/net/tfminecraft/magic/integration/SkillIdResolver.java +++ b/src/main/java/net/tfminecraft/magic/integration/SkillIdResolver.java @@ -1,22 +1,40 @@ package net.tfminecraft.magic.integration; +import java.util.Locale; + import io.lumine.mythic.lib.skill.Skill; import io.lumine.mythic.lib.skill.handler.SkillHandler; import io.lumine.mythic.lib.skill.trigger.TriggerType; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.skill.CastableSkill; import net.Indyuce.mmocore.skill.RegisteredSkill; +import net.tfminecraft.magic.Cache; public final class SkillIdResolver { private SkillIdResolver() {} + /** + * True for MMOCore casts and for every trigger named in {@code runes.keybinds}. + * Rune abilities report their keybind ({@code RIGHT_CLICK} and the rest), not {@code CAST}. + */ public static boolean isActiveCast(Skill cast) { if (cast == null) { return false; } TriggerType trigger = cast.getTrigger(); - return trigger == TriggerType.CAST || trigger == TriggerType.API; + if (trigger == null) { + return false; + } + if (trigger == TriggerType.CAST || trigger == TriggerType.API) { + return true; + } + String name = trigger.name(); + if (name != null && Cache.castTriggers.contains(name.toUpperCase(Locale.ROOT))) { + return true; + } + String lower = trigger.getLowerCaseId(); + return lower != null && Cache.castTriggers.contains(lower.toUpperCase(Locale.ROOT)); } /** diff --git a/src/main/java/net/tfminecraft/magic/loader/ConfigLoader.java b/src/main/java/net/tfminecraft/magic/loader/ConfigLoader.java index 4bcdb7d..feca373 100644 --- a/src/main/java/net/tfminecraft/magic/loader/ConfigLoader.java +++ b/src/main/java/net/tfminecraft/magic/loader/ConfigLoader.java @@ -2,6 +2,7 @@ import java.io.File; import java.io.IOException; +import java.util.List; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.InvalidConfigurationException; @@ -89,6 +90,7 @@ public boolean loadSafe(File configFile) { long seconds = cast.getLong("refuse_chat_seconds", 30L); Cache.refuseChatMillis = Math.max(0L, seconds) * 1000L; } + loadCastTriggers(config.getStringList("runes.keybinds")); ConfigurationSection artifacts = config.getConfigurationSection("artifacts"); if (artifacts != null) { @@ -111,6 +113,15 @@ public boolean loadSafe(File configFile) { return true; } + private static void loadCastTriggers(List keybinds) { + Cache.setCastTriggers(keybinds); + if (Cache.castTriggers.isEmpty()) { + Magic.plugin.getLogger().info("[Magic] No rune keybinds configured; cast checks use CAST and API."); + return; + } + Magic.plugin.getLogger().info("[Magic] Cast triggers: CAST, API, " + String.join(", ", Cache.castTriggers)); + } + private static void loadGear(ConfigurationSection section) { if (section == null) { return; diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 32924d6..b43d671 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -25,6 +25,16 @@ equilibrium: 0: { mana: 0, damage: 0, cooldown: 0 } 100: { mana: -0.20, damage: 0.25, cooldown: -0.05 } +# MythicLib triggers the cast listener treats as a spell, besides CAST and API. +# These match the keybinds a rune can be set to. A weapon still has to be attuned +# to the spell's element. +runes: + keybinds: + - RIGHT_CLICK + - LEFT_CLICK + - SHIFT_RIGHT_CLICK + - SHIFT_LEFT_CLICK + # Global tick cycle (one runnable for the whole plugin). # interval_ticks: 20 = one second elapsed per tick. # seconds_per_hour: real seconds that count as one in-game hour for per-hour rates @@ -65,7 +75,7 @@ meditation: cast: refuse_chat_seconds: 30 -# Drift from spending mana on a CAST/API skill. 40 mana -> 0.04 (mana / mana_divisor). +# Drift from spending mana on a gated cast. 40 mana -> 0.04 (mana / mana_divisor). # Surge adds corruption (negative equilibrium). Flow adds tranquility (positive). cast_drift: mana_divisor: 1000