Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/main/java/net/tfminecraft/magic/Cache.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package net.tfminecraft.magic;

import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;

import io.lumine.mythic.lib.skill.trigger.TriggerType;

/**
* Runtime flags from config.yml.
*/
Expand Down Expand Up @@ -36,6 +39,9 @@ public final class Cache {
*/
public static volatile Set<String> castTriggers = Set.of();

public static Set<String> runeTypes = new HashSet<>();
public static Set<TriggerType> runeKeybinds = new LinkedHashSet<>();

public static double tickIntervalSeconds() {
return Math.max(1L, tickIntervalTicks) / 20.0;
}
Expand Down
1 change: 1 addition & 0 deletions src/main/java/net/tfminecraft/magic/Magic.java
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ private void createConfigs() {
"gear/parts.yml",
"gear/socket-colours.yml",
"gear/orbs.yml",
"gear/model-schemes.yml",
"elements/elements.yml",
"artifacts/generator.yml",
"artifacts/model-schemes.yml",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,7 @@ public static String kindForPath(ArtifactTypeDef type, String path) {
private static String buildDisplayName(ArtifactTypeDef type, String baseName) {
String rolledName = baseName != null && !baseName.isBlank() ? baseName : type.getElementId();
ElementDef element = ElementRegistry.getById(type.getElementId());
String elementColor = element != null && element.getColor() != null && !element.getColor().isBlank()
? element.getColor()
: "#ffffff";
return MagicText.format(elementColor + rolledName);
return MagicText.elementText(element, rolledName);
}

private static void applyName(MMOItem mmo, String displayName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;

import net.tfminecraft.magic.model.ElementDef;
import net.tfminecraft.magic.registry.ElementRegistry;
import net.tfminecraft.magic.session.ResonanceSession;

public final class AttunementCaptureService {
Expand All @@ -22,6 +24,10 @@ public static void credit(
if (session == null || elementId == null || elementId.isBlank() || gain <= EPSILON) {
return;
}
ElementDef element = ElementRegistry.getById(elementId.toLowerCase(java.util.Locale.ROOT));
if (element != null && !element.isUnlocked(meditator)) {
return;
}
double before = session.getResonance(elementId);
session.addResonance(elementId, gain);
AuraLog.append(
Expand Down
146 changes: 134 additions & 12 deletions src/main/java/net/tfminecraft/magic/command/MagicCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

import io.lumine.mythic.lib.skill.trigger.TriggerType;
import net.tfminecraft.magic.Cache;
import net.tfminecraft.magic.Magic;
import net.tfminecraft.magic.Messages;
Expand All @@ -35,6 +36,7 @@
import net.tfminecraft.magic.artifact.shrine.ShrineChargeService;
import net.tfminecraft.magic.attunement.AuraLog;
import net.tfminecraft.magic.gear.GearRefresher;
import net.tfminecraft.magic.gear.RuneKeybind;
import net.tfminecraft.magic.model.ElementDef;
import net.tfminecraft.magic.profile.MagicProfileService;
import net.tfminecraft.magic.registry.ElementRegistry;
Expand All @@ -48,13 +50,23 @@ public final class MagicCommand implements CommandExecutor, TabCompleter {

@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!hasAdmin(sender)) {
sender.sendMessage(Messages.get("admin.no_permission"));
if (args.length == 0) {
if (hasAdmin(sender)) {
sender.sendMessage(Messages.get("admin.usage"));
} else if (hasRuneKeybind(sender)) {
sender.sendMessage(Messages.get("rune.usage"));
} else {
sender.sendMessage(Messages.get("admin.no_permission"));
}
return true;
}

if (args.length == 0) {
sender.sendMessage(Messages.get("admin.usage"));
if ("rune".equalsIgnoreCase(args[0])) {
return handleRune(sender, args);
}

if (!hasAdmin(sender)) {
sender.sendMessage(Messages.get("admin.no_permission"));
return true;
}

Expand Down Expand Up @@ -97,6 +109,38 @@ public boolean onCommand(CommandSender sender, Command command, String label, St
return true;
}

private static boolean handleRune(CommandSender sender, String[] args) {
if (!(sender instanceof Player player)) {
sender.sendMessage(Messages.get("rune.players_only"));
return true;
}
if (!hasRuneKeybind(sender)) {
sender.sendMessage(Messages.get("rune.no_permission"));
return true;
}
if (args.length < 3 || !"keybind".equalsIgnoreCase(args[1])) {
sender.sendMessage(Messages.get("rune.usage"));
return true;
}
TriggerType trigger = resolveRuneKeybind(args[2]);
if (trigger == null) {
sender.sendMessage(Messages.get("rune.unknown_trigger"));
return true;
}
ItemStack held = player.getInventory().getItemInMainHand();
RuneKeybind.Outcome outcome = RuneKeybind.apply(held, trigger);
switch (outcome.result()) {
case NOT_RUNE -> sender.sendMessage(Messages.get("rune.not_a_rune"));
case NO_ABILITIES -> sender.sendMessage(Messages.get("rune.no_abilities"));
case FAILED -> sender.sendMessage(Messages.get("rune.failed"));
case OK -> {
player.getInventory().setItemInMainHand(outcome.item());
sender.sendMessage(Messages.get("rune.success", "trigger", trigger.name()));
}
}
return true;
}

private static boolean handleRefresh(CommandSender sender) {
if (!(sender instanceof Player player)) {
sender.sendMessage(Messages.get("gear.refresh.players_only"));
Expand Down Expand Up @@ -198,7 +242,7 @@ private static boolean handleResonance(CommandSender sender, String[] args) {
}
if ("reset".equals(action)) {
String elementArg = args.length >= 4 ? args[3] : "all";
if (!applyResonance(session, elementArg, Cache.defaultResonance, false, sender)) {
if (!applyResonance(target, session, elementArg, Cache.defaultResonance, false, sender)) {
return true;
}
persistResonance(target, session);
Expand All @@ -219,7 +263,7 @@ private static boolean handleResonance(CommandSender sender, String[] args) {
return true;
}
if ("set".equals(action)) {
if (!applyResonance(session, elementArg, amount, false, sender)) {
if (!applyResonance(target, session, elementArg, amount, false, sender)) {
return true;
}
persistResonance(target, session);
Expand All @@ -231,7 +275,7 @@ private static boolean handleResonance(CommandSender sender, String[] args) {
return true;
}
if ("add".equals(action)) {
if (!applyResonance(session, elementArg, amount, true, sender)) {
if (!applyResonance(target, session, elementArg, amount, true, sender)) {
return true;
}
persistResonance(target, session);
Expand Down Expand Up @@ -287,6 +331,7 @@ private static boolean handleShrine(CommandSender sender, String[] args) {
}

private static boolean applyResonance(
Player target,
ResonanceSession session,
String elementArg,
double amount,
Expand All @@ -297,22 +342,49 @@ private static boolean applyResonance(
return false;
}
if ("all".equalsIgnoreCase(elementArg)) {
List<String> skipped = new ArrayList<>();
int applied = 0;
for (ElementDef element : ElementRegistry.getAll()) {
double next = add ? session.getResonance(element.getId()) + amount : amount;
double current = session.getResonance(element.getId());
double next = add ? current + amount : amount;
if (isGain(current, next) && !element.isUnlocked(target)) {
skipped.add(element.getId());
continue;
}
session.setResonance(element.getId(), next);
applied++;
}
return true;
if (!skipped.isEmpty()) {
sender.sendMessage(Messages.get(
"resonance.admin.locked_skipped",
"player", target.getName(),
"elements", String.join(", ", skipped)));
}
return applied > 0;
}
ElementDef element = ElementRegistry.getById(elementArg.toLowerCase(Locale.ROOT));
if (element == null) {
sender.sendMessage(Messages.get("resonance.admin.unknown_element"));
return false;
}
double next = add ? session.getResonance(element.getId()) + amount : amount;
double current = session.getResonance(element.getId());
double next = add ? current + amount : amount;
if (isGain(current, next) && !element.isUnlocked(target)) {
sender.sendMessage(Messages.get(
"resonance.admin.locked",
"player", target.getName(),
"permission", element.getPermission(),
"element", element.getId()));
return false;
}
session.setResonance(element.getId(), next);
return true;
}

private static boolean isGain(double current, double next) {
return next > current + 0.0001;
}

private static void persistResonance(Player target, ResonanceSession session) {
MagicProfileService profiles = Magic.plugin.getProfileService();
if (profiles != null) {
Expand Down Expand Up @@ -533,13 +605,63 @@ private static boolean hasAdmin(CommandSender sender) {
return sender.hasPermission("magic.admin") || sender.hasPermission("magic.admin.reload");
}

private static boolean hasRuneKeybind(CommandSender sender) {
return sender.hasPermission("magic.rune.keybind");
}

private static TriggerType resolveRuneKeybind(String raw) {
if (raw == null || raw.isBlank()) {
return null;
}
String want = raw.trim().toUpperCase(Locale.ROOT);
for (TriggerType type : Cache.runeKeybinds) {
if (type != null && want.equals(type.name())) {
return type;
}
}
return null;
}

@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
if (!hasAdmin(sender) || args.length == 0) {
if (args.length == 0) {
return Collections.emptyList();
}
boolean admin = hasAdmin(sender);
boolean rune = hasRuneKeybind(sender);
if (!admin && !rune) {
return Collections.emptyList();
}
if (args.length == 1) {
return filterPrefix(SUBCOMMANDS, args[0]);
List<String> options = new ArrayList<>();
if (rune) {
options.add("rune");
}
if (admin) {
options.addAll(SUBCOMMANDS);
}
return filterPrefix(options, args[0]);
}
if ("rune".equalsIgnoreCase(args[0])) {
if (!rune) {
return Collections.emptyList();
}
if (args.length == 2) {
return filterPrefix(List.of("keybind"), args[1]);
}
if (args.length == 3 && "keybind".equalsIgnoreCase(args[1])) {
List<String> names = new ArrayList<>();
for (TriggerType type : Cache.runeKeybinds) {
if (type != null && type.name() != null) {
names.add(type.name());
}
}
return filterPrefix(names, args[2]);
}
return Collections.emptyList();
}
if (!admin) {
return Collections.emptyList();
}
if ("fillchest".equalsIgnoreCase(args[0])) {
if (args.length == 2) {
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/net/tfminecraft/magic/gear/ArchetypeDef.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public final class ArchetypeDef {
private final GearType type;
private final String name;
private final String template;
private final String icon;
private final boolean melee;
private final List<String> required;
private final Map<String, String> slots;
Expand All @@ -23,12 +24,14 @@ public ArchetypeDef(
GearType type,
String name,
String template,
String icon,
boolean melee,
List<String> required,
Map<String, String> slots) {
this.type = type;
this.name = name == null || name.isBlank() ? type.getDisplayName() : name;
this.template = template == null ? "" : template.trim();
this.icon = icon == null ? "" : icon.trim();
this.melee = melee;
this.required = List.copyOf(required == null ? List.of() : required);
Map<String, String> copy = new LinkedHashMap<>();
Expand All @@ -55,6 +58,10 @@ public String getTemplate() {
return template;
}

public String getIcon() {
return icon;
}

public boolean isMelee() {
return melee;
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/net/tfminecraft/magic/gear/GearCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ public final class GearCache {

public static boolean alignmentEnabled = false;

/** When false, gem socket colour strings omit Common/Rare/Epic/Legendary prefix. */
public static boolean socketRarityPrefix = true;

/** Weapon tier band in the spell's element -> bonus. Never a penalty. */
private static final Map<Integer, ModifierTriple> ALIGNMENT = new LinkedHashMap<>();

Expand Down
Loading