mirror of
https://gitlab.com/phoenix-dvpmt/mmoitems.git
synced 2025-01-02 06:27:42 +01:00
DamageTypeRestriction modifier for ATTACK ability trigger, very fun to use yes. Includes an advanced translation system to express this modifier.
This commit: (1) Allows the GUI to correctly display the DamageTypeRestriction modifier (2) Includes code to parse input for the DamageTypeRestriction (3) Translation system to display the trigger in a more user-friendly manner (4) Fixes offhand abilities firing even when encumbered by two-handedness
This commit is contained in:
parent
7c243e4ac8
commit
0994e6b0c3
@ -1,6 +1,7 @@
|
||||
package net.Indyuce.mmoitems.api.player;
|
||||
|
||||
import io.lumine.mythic.lib.MythicLib;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.MythicCraftingManager;
|
||||
import io.lumine.mythic.lib.api.item.NBTItem;
|
||||
import io.lumine.mythic.lib.api.player.EquipmentSlot;
|
||||
import io.lumine.mythic.lib.api.player.MMOPlayerData;
|
||||
@ -216,11 +217,18 @@ public class PlayerData {
|
||||
final VolatileMMOItem item = equipped.getCached();
|
||||
|
||||
// Abilities
|
||||
if (item.hasData(ItemStats.ABILITIES))
|
||||
if (item.hasData(ItemStats.ABILITIES) &&
|
||||
|
||||
// Do not add this ability if it is offhanded and offhand abilities are disabled
|
||||
!(equipped.getSlot() == EquipmentSlot.OFF_HAND && MMOItems.plugin.getLanguage().disableOffhandAbilities) &&
|
||||
|
||||
// Do not add this ability if it is either of the hand slots, and the player is encumbered, and abilities don't bypass encumbering
|
||||
!((equipped.getSlot() == EquipmentSlot.MAIN_HAND || equipped.getSlot() == EquipmentSlot.OFF_HAND) &&
|
||||
isEncumbered() && !MMOItems.plugin.getLanguage().abilitiesBypassEncumbering))
|
||||
|
||||
for (AbilityData abilityData : ((AbilityListData) item.getData(ItemStats.ABILITIES)).getAbilities()) {
|
||||
ModifierSource modSource = equipped.getCached().getType().getModifierSource();
|
||||
mmoData.getPassiveSkillMap().addModifier(new PassiveSkill("MMOItemsItem", abilityData, equipped.getSlot(), modSource));
|
||||
}
|
||||
mmoData.getPassiveSkillMap().addModifier(new PassiveSkill("MMOItemsItem", abilityData, equipped.getSlot(), modSource));}
|
||||
|
||||
// Modifier application rules
|
||||
final ModifierSource source = item.getType().getModifierSource();
|
||||
|
@ -0,0 +1,445 @@
|
||||
package net.Indyuce.mmoitems.api.util;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import io.lumine.mythic.lib.damage.DamageType;
|
||||
import io.lumine.mythic.lib.skill.trigger.TriggerType;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* The DamageTypeRestriction [DTR] modifier has several built-in features, so it's easier
|
||||
* to keep them all in this one place, since they also include functions to use them:
|
||||
* <br><br>
|
||||
* + Translate the trigger name depending on the DTR <br>
|
||||
* + Change color of damage types / scalings in modifiers <br>
|
||||
* +
|
||||
*/
|
||||
public class DamageTypeRestrictionSettings {
|
||||
|
||||
/**
|
||||
* Read the values directly off a configuration section
|
||||
*
|
||||
* @param config The configuration section, supposedly in the plugin config.yml
|
||||
*/
|
||||
public DamageTypeRestrictionSettings(@Nullable ConfigurationSection config) {
|
||||
if (config == null) {
|
||||
//DTR//MythicCraftingManager.log("\u00a78DTR\u00a7a SET\u00a7c Null config provided");
|
||||
return; }
|
||||
|
||||
/*
|
||||
* damage-type-restrictions:
|
||||
*
|
||||
* damage-type-translations:
|
||||
* MAGIC: "Magic"
|
||||
* PHYSICAL: "Melee"
|
||||
* PROJECTILE: "Ranged"
|
||||
* WEAPON: "Weapon"
|
||||
* SKILL: "Skill"
|
||||
* UNARMED: "Unarmed"
|
||||
* ON_HIT: "Reaction"
|
||||
* MINION: "Minion"
|
||||
* DOT: "Lingering"
|
||||
*
|
||||
* attack-type-translations:
|
||||
* WEAPON: "Attack"
|
||||
* SKILL: "Ability Hit"
|
||||
* NEITHER: "Damage"
|
||||
* BOTH: "Ability-Assisted Attack"
|
||||
*
|
||||
* damage-type-colors:
|
||||
* MAGIC: "&9"
|
||||
* PHYSICAL: "&8"
|
||||
* WEAPON: "&7"
|
||||
* SKILL: "&f"
|
||||
* PROJECTILE: "&a"
|
||||
* UNARMED: "&e"
|
||||
* ON_HIT: "&0"
|
||||
* MINION: "&d"
|
||||
* DOT: "&3"
|
||||
*/
|
||||
ConfigurationSection damageTypeTranslations = config.isConfigurationSection("damage-type-translations") ? config.getConfigurationSection("damage-type-translations") : null;
|
||||
ConfigurationSection attackTypeTranslations = config.isConfigurationSection("attack-type-translations") ? config.getConfigurationSection("attack-type-translations") : null;
|
||||
if (damageTypeTranslations == null || attackTypeTranslations == null) { advancedTriggerDisplay = false; } else {
|
||||
//DTR//MythicCraftingManager.log("\u00a78DTR\u00a7a SET\u00a7a Accepted advanced trigger display");
|
||||
|
||||
// Both are defined, use advanced trigger display
|
||||
advancedTriggerDisplay = true;
|
||||
|
||||
// Translate damage types
|
||||
damageNames.put(DamageType.MAGIC, damageTypeTranslations.getString("MAGIC", "Magic"));
|
||||
damageNames.put(DamageType.PHYSICAL, damageTypeTranslations.getString("PHYSICAL", "Melee"));
|
||||
damageNames.put(DamageType.PROJECTILE, damageTypeTranslations.getString("PROJECTILE", "Projectile"));
|
||||
damageNames.put(DamageType.WEAPON, damageTypeTranslations.getString("WEAPON", "Weapon"));
|
||||
damageNames.put(DamageType.SKILL, damageTypeTranslations.getString("SKILL", "Skill"));
|
||||
damageNames.put(DamageType.UNARMED, damageTypeTranslations.getString("UNARMED", "Unarmed"));
|
||||
damageNames.put(DamageType.ON_HIT, damageTypeTranslations.getString("ON_HIT", "Reaction"));
|
||||
damageNames.put(DamageType.MINION, damageTypeTranslations.getString("MINION", "Minion"));
|
||||
damageNames.put(DamageType.DOT, damageTypeTranslations.getString("DOT", "Lingering"));
|
||||
|
||||
// Translate attack types
|
||||
attackNames.put(AttackType.WEAPON, attackTypeTranslations.getString("WEAPON", "Attack"));
|
||||
attackNames.put(AttackType.SKILL, attackTypeTranslations.getString("SKILL", "Ability Hit"));
|
||||
attackNames.put(AttackType.BOTH, attackTypeTranslations.getString("BOTH", "Ability-Assisted Attack"));
|
||||
attackNames.put(AttackType.NEITHER, attackTypeTranslations.getString("NEITHER", "Damage"));
|
||||
}
|
||||
|
||||
ConfigurationSection damageTypeColors = config.isConfigurationSection("damage-type-colors") ? config.getConfigurationSection("damage-type-colors") : null;
|
||||
if (damageTypeColors == null) { advancedRecoloring = false; } else {
|
||||
//DTR//MythicCraftingManager.log("\u00a78DTR\u00a7a SET\u00a7a Accepted advanced recoloring");
|
||||
|
||||
// Colors are defined
|
||||
advancedRecoloring = true;
|
||||
|
||||
// This information actually already exists
|
||||
for (DamageType dt : DamageType.values()) {
|
||||
|
||||
// Well is there an override?
|
||||
String colour = damageTypeColors.getString(dt.toString(), null);
|
||||
if (colour == null) { continue; }
|
||||
|
||||
// Just match the name of the damage type, default to its default color
|
||||
damageColor.put(dt, colour);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If trigger names will be translated depending on the DTR, this has the
|
||||
* advantage of being user-friendly at the disadvantage of having to configure
|
||||
* names of each modifier combination ~ WEAPON MAGIC !SKILL for example would be
|
||||
* 'Staff Attack'
|
||||
*/
|
||||
public boolean isAdvancedTriggerDisplay() { return advancedTriggerDisplay; }
|
||||
boolean advancedTriggerDisplay = true;
|
||||
|
||||
/**
|
||||
* @return If the color of damage type modifiers in modifier names
|
||||
* and trigger names will be recolored when actually displaying
|
||||
* into the item.
|
||||
*/
|
||||
public boolean isAdvancedRecoloring() { return advancedRecoloring; }
|
||||
boolean advancedRecoloring = true;
|
||||
|
||||
/**
|
||||
* Example:
|
||||
* PROJECTILE -> Ranged
|
||||
*
|
||||
* @param type The damage type you intend to translate
|
||||
*
|
||||
* @return The player-friendly name off this damage type
|
||||
*/
|
||||
@NotNull public String getDamageName(@NotNull DamageType type) { return damageNames.getOrDefault(type, SilentNumbers.titleCaseConversion(type.toString().replace("-", " ").replace("_", " "))); }
|
||||
@NotNull HashMap<DamageType, String> damageNames = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Example:
|
||||
* SKILL -> Ability Hit
|
||||
*
|
||||
* @param type The attack type you intend to translate
|
||||
*
|
||||
* @return The player-friendly name off this attack type
|
||||
*/
|
||||
@NotNull public String getAttackName(@NotNull AttackType type) { return attackNames.getOrDefault(type, "Damage"); }
|
||||
@NotNull HashMap<AttackType, String> attackNames = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Example:
|
||||
* SKILL &f (default) -> <#FEEFEF> (specified in config)
|
||||
*
|
||||
* @param type The damage you intend to get its color
|
||||
*
|
||||
* @return The 'translated' color of this damage type, to
|
||||
* override the default damage colors mostly.
|
||||
*/
|
||||
@NotNull public String getDamageColor(@NotNull DamageType type) { return damageColor.getOrDefault(type, SilentNumbers.titleCaseConversion(type.toString().replace("-", " ").replace("_", " "))); }
|
||||
@NotNull HashMap<DamageType, String> damageColor = new HashMap<>();
|
||||
|
||||
@NotNull public static final String SKMOD_DAMAGE_TYPE_DAMAGE = "\u00a7o■";
|
||||
@NotNull public static final String SKMOD_DAMAGE_TYPE_BLACK = "\u00a7c!";
|
||||
@NotNull public static final String SKMOD_DAMAGE_TYPE_AND = "\u00a77 ";
|
||||
@NotNull public static final String SKMOD_DAMAGE_TYPE_COMMA = "\u00a77,";
|
||||
@NotNull public static final String SKMOD_DAMAGE_TYPE_OR = "\u00a77/";
|
||||
|
||||
/**
|
||||
* Usually the displayed name of the trigger is just... the name of the trigger.
|
||||
* <p>
|
||||
* However, when using the Damage Type skill modifier, this can be misleading;
|
||||
* for example, the {@link TriggerType#ATTACK} will no longer trigger with any attack.
|
||||
* </p>
|
||||
* This method will rename it correctly; for example: WEAPON MAGIC = Magic Attack
|
||||
* <p></p>
|
||||
* This only supports the trigger {@link TriggerType#ATTACK}
|
||||
*
|
||||
* @param trigger The trigger by which this skill fires
|
||||
* @param attackType The encoded skill modifier value
|
||||
*
|
||||
* @return The way the trigger should display in lore.
|
||||
*/
|
||||
@NotNull public String getTriggerDisplayName(@NotNull TriggerType trigger, double attackType) {
|
||||
|
||||
// Use the default?
|
||||
String triggerDisplayName = MMOItems.plugin.getLanguage().getCastingModeName(trigger);
|
||||
|
||||
// If no skill modifiers are used, or the config option is disabled
|
||||
if (attackType == 0 || !isAdvancedTriggerDisplay()) {
|
||||
//APP//MythicCraftingManager.log("\u00a78ABT\u00a73 APP\u00a7c No advanced trigger display\u00a7e " + attackType);
|
||||
return triggerDisplayName; }
|
||||
|
||||
// Currently, only ATTACK trigger is supported
|
||||
if (!TriggerType.ATTACK.equals(trigger)) {
|
||||
//APP//MythicCraftingManager.log("\u00a78ABT\u00a73 APP\u00a7c Not a supported trigger");
|
||||
return triggerDisplayName; }
|
||||
|
||||
boolean named = false;
|
||||
boolean orMode = false;
|
||||
if (attackType < 0) { orMode = true; attackType *= -1; }
|
||||
String separatorSymbol = (orMode ? SKMOD_DAMAGE_TYPE_OR : SKMOD_DAMAGE_TYPE_AND);
|
||||
|
||||
// Decode
|
||||
ArrayList<DamageType> white = DamageType.getWhitelist(attackType);
|
||||
ArrayList<DamageType> black = DamageType.getBlacklist(attackType);
|
||||
|
||||
// Currently, only ATTACK trigger is supported
|
||||
if (white.isEmpty()) {
|
||||
//APP//MythicCraftingManager.log("\u00a78ABT\u00a73 APP\u00a7c Empty whitelist, blacklist is not supported");
|
||||
return triggerDisplayName; }
|
||||
|
||||
// Special names sector
|
||||
if (TriggerType.ATTACK.equals(trigger)) {
|
||||
//APP//MythicCraftingManager.log("\u00a78ABT\u00a73 APP\u00a77 Identified as the ATTACK trigger");
|
||||
|
||||
// Very specific overrides
|
||||
if (white.size() == 1 && white.contains(DamageType.MINION)) {
|
||||
|
||||
// Minion Attack
|
||||
triggerDisplayName = getDamageName(DamageType.MINION) + SKMOD_DAMAGE_TYPE_AND + getAttackName(AttackType.WEAPON);
|
||||
named = true;
|
||||
|
||||
} else if (white.size() == 1 && white.contains(DamageType.DOT)) {
|
||||
|
||||
// Lingering Attack
|
||||
triggerDisplayName = getDamageName(DamageType.DOT) + SKMOD_DAMAGE_TYPE_AND + getAttackName(AttackType.WEAPON);
|
||||
named = true;
|
||||
|
||||
} else if (white.size() == 2 && white.contains(DamageType.MINION) && white.contains(DamageType.PROJECTILE)) {
|
||||
|
||||
// Minion Ranged Attack
|
||||
triggerDisplayName = getDamageName(DamageType.MINION) + SKMOD_DAMAGE_TYPE_AND + getDamageName(DamageType.PROJECTILE) + SKMOD_DAMAGE_TYPE_AND + getAttackName(AttackType.WEAPON);
|
||||
named = true;
|
||||
|
||||
} else if (white.size() == 2 && white.contains(DamageType.MINION) && white.contains(DamageType.MAGIC)) {
|
||||
|
||||
// Minion Magic Attack
|
||||
triggerDisplayName = getDamageName(DamageType.MINION) + SKMOD_DAMAGE_TYPE_AND + getDamageName(DamageType.MAGIC) + SKMOD_DAMAGE_TYPE_AND + getAttackName(AttackType.WEAPON);
|
||||
named = true;
|
||||
|
||||
} else {
|
||||
|
||||
// Skill, Attack, or generic damage
|
||||
boolean isWeapon = white.contains(DamageType.WEAPON);
|
||||
boolean isSkill = white.contains(DamageType.SKILL);
|
||||
boolean both = isWeapon && isSkill;
|
||||
boolean neither = !isWeapon && !isSkill;
|
||||
|
||||
// Elemental type
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (DamageType whitelisted : white) {
|
||||
|
||||
// Ignore weapon and skill
|
||||
if (whitelisted == DamageType.WEAPON ||
|
||||
whitelisted == DamageType.SKILL) {
|
||||
continue; }
|
||||
|
||||
// Append separator
|
||||
if (builder.length() > 1) { builder.append(separatorSymbol); }
|
||||
|
||||
// Append the type
|
||||
builder
|
||||
//.append(damageColors(whitelisted.getColor())) // Sawala doesn't think colours are gud
|
||||
.append(getDamageName(whitelisted));
|
||||
}
|
||||
|
||||
String latter;
|
||||
String built = builder.toString();
|
||||
String former = "";
|
||||
|
||||
// Requires any other damage type?
|
||||
if (built.length() > 0) {
|
||||
if (neither) { latter = SKMOD_DAMAGE_TYPE_AND + getAttackName(AttackType.NEITHER); }
|
||||
else if (both) { latter = SKMOD_DAMAGE_TYPE_AND + getAttackName(AttackType.BOTH); }
|
||||
else if (isWeapon) { latter = SKMOD_DAMAGE_TYPE_AND + getAttackName(AttackType.WEAPON); }
|
||||
else { latter = SKMOD_DAMAGE_TYPE_AND + getAttackName(AttackType.SKILL); }
|
||||
|
||||
// Only characterized by weapon
|
||||
} else {
|
||||
|
||||
if (neither) { latter = getAttackName(AttackType.NEITHER); }
|
||||
else if (both) { latter = getAttackName(AttackType.BOTH); }
|
||||
else if (isWeapon) { latter = getAttackName(AttackType.WEAPON); }
|
||||
else { latter = getAttackName(AttackType.SKILL); }
|
||||
}
|
||||
|
||||
//APP//MythicCraftingManager.log("\u00a78ABT\u00a73 APP\u00a77 Former:\u00a73 " + former);
|
||||
//APP//MythicCraftingManager.log("\u00a78ABT\u00a73 APP\u00a77 Built:\u00a73 " + built);
|
||||
//APP//MythicCraftingManager.log("\u00a78ABT\u00a73 APP\u00a77 Latter:\u00a73 " + latter);
|
||||
|
||||
named = true;
|
||||
triggerDisplayName = former + built + latter;
|
||||
}
|
||||
}
|
||||
|
||||
// Just display the damage type restriction as squares
|
||||
else if (TriggerType.KILL_ENTITY.equals(trigger)) {
|
||||
|
||||
// Elemental type
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (DamageType whitelisted : white) {
|
||||
|
||||
// Append separator
|
||||
if (builder.length() > 1) { builder.append(separatorSymbol); }
|
||||
|
||||
// Append the type
|
||||
builder
|
||||
//.append(damageColors(whitelisted.getColor())) // Sawala doesn't think colours are gud
|
||||
.append(getDamageName(whitelisted));
|
||||
}
|
||||
|
||||
named = true;
|
||||
triggerDisplayName = builder + SKMOD_DAMAGE_TYPE_AND + triggerDisplayName;
|
||||
}
|
||||
|
||||
// No special name just default it
|
||||
if (!named) {
|
||||
|
||||
// Append that
|
||||
triggerDisplayName += " " + damageTypeRestrictionDisplay(separatorSymbol, white, black);
|
||||
}
|
||||
|
||||
return triggerDisplayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param damageTypeRestriction Number that encodes for the damage type restriction
|
||||
*
|
||||
* @return A nice chain of colored boxes (real) that represents this damage type restriction.
|
||||
*/
|
||||
@NotNull String damageTypeRestrictionDisplay(double damageTypeRestriction) {
|
||||
|
||||
boolean orMode = false;
|
||||
if (damageTypeRestriction < 0) { orMode = true; damageTypeRestriction *= -1; }
|
||||
String separatorSymbol = (orMode ? SKMOD_DAMAGE_TYPE_OR : SKMOD_DAMAGE_TYPE_AND);
|
||||
|
||||
// Decode
|
||||
ArrayList<DamageType> white = DamageType.getWhitelist(damageTypeRestriction);
|
||||
ArrayList<DamageType> black = DamageType.getBlacklist(damageTypeRestriction);
|
||||
|
||||
// Just build the string man
|
||||
return damageTypeRestrictionDisplay(separatorSymbol, white, black);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param separatorSymbol Separator symbol to use between whitelisted damage types
|
||||
* @param white Damage types whitelisted
|
||||
* @param black Damage types blacklisted
|
||||
*
|
||||
* @return A nice chain of colored boxes (real) that represents this damage type restriction.
|
||||
*/
|
||||
@NotNull String damageTypeRestrictionDisplay(@NotNull String separatorSymbol, @NotNull ArrayList<DamageType> white, @NotNull ArrayList<DamageType> black) {
|
||||
|
||||
StringBuilder append = new StringBuilder();
|
||||
for (DamageType w : white) {
|
||||
|
||||
// Append separator
|
||||
if (append.length() > 1) { append.append(separatorSymbol); }
|
||||
|
||||
// Append damage
|
||||
append.append(damageColors(w.getColor())).append(SKMOD_DAMAGE_TYPE_DAMAGE);
|
||||
}
|
||||
|
||||
// Separator for blacklist
|
||||
if (append.length() > 1 && black.size() > 0) { append.append(SKMOD_DAMAGE_TYPE_COMMA); }
|
||||
|
||||
for (DamageType b : black) {
|
||||
|
||||
// Append separator
|
||||
if (append.length() > 1) { append.append(SKMOD_DAMAGE_TYPE_AND); }
|
||||
|
||||
// Append damage
|
||||
append.append(SKMOD_DAMAGE_TYPE_BLACK).append(damageColors(b.getColor())).append(SKMOD_DAMAGE_TYPE_DAMAGE);
|
||||
}
|
||||
|
||||
return append.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param in The colored string in the default format
|
||||
*
|
||||
* @return Color overridden by user-specified counterpart.
|
||||
*/
|
||||
@NotNull public String damageColors(@Nullable String in) {
|
||||
//SDC//MythicCraftingManager.log("\u00a78ABT\u00a7c SDC\u00a77 Recoloring\u00a7b " + in);
|
||||
if (in == null) { return ""; }
|
||||
if (!isAdvancedRecoloring()) { return in; }
|
||||
|
||||
/*
|
||||
* Sawala's agony color replacements
|
||||
*
|
||||
* Because I literally had everything consistent in &8 &a &9 and he was like &4 &2 <HEX0038C2>;
|
||||
* what on earth even is <HEX0038C2> ffs for magic damage some ugly ass deep blue (I sleep)
|
||||
*/
|
||||
for (DamageType ty : damageColor.keySet()) {
|
||||
|
||||
//SDC//MythicCraftingManager.log("\u00a78ABT\u00a7c SDC\u00a7e +\u00a77 Damage Type\u00a7b " + ty.toString());
|
||||
//SDC//MythicCraftingManager.log("\u00a78ABT\u00a7c SDC\u00a7e +\u00a77 Default Col\u00a7b " + ty.getColor() + "O");
|
||||
//SDC//MythicCraftingManager.log("\u00a78ABT\u00a7c SDC\u00a7e +\u00a77 Override Cl\u00a7b " + getDamageColor(ty) + "O");
|
||||
|
||||
// Both & and §
|
||||
in = in.replace(ty.getColor().replace('\u00a7', '&'), getDamageColor(ty));
|
||||
in = in.replace(ty.getColor(), getDamageColor(ty));
|
||||
}
|
||||
//SDC//MythicCraftingManager.log("\u00a78ABT\u00a7c SDC\u00a77 Result\u00a7b " + in);
|
||||
|
||||
// he he ha ha
|
||||
return in;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A way to classify and translate the result of an {@link io.lumine.mythic.lib.damage.AttackMetadata},
|
||||
*/
|
||||
enum AttackType {
|
||||
|
||||
/**
|
||||
* Dealing damage with weapons (real), Attacks.
|
||||
*/
|
||||
WEAPON,
|
||||
|
||||
/**
|
||||
* Dealing damage with abilities, Ability Hits.
|
||||
*/
|
||||
SKILL,
|
||||
|
||||
/**
|
||||
* No information on skill or weapon damage,
|
||||
* treated as just generic Damage.
|
||||
*/
|
||||
NEITHER,
|
||||
|
||||
/**
|
||||
* Both skill and weapon damage types are present,
|
||||
* this would make sense if the lore of the ability
|
||||
* implies that your weapon is being used in the attack.
|
||||
* <br><br>
|
||||
* Compare simply casting a fireball, vs simply hitting
|
||||
* with a baton, vs coating the baton with magic fire and
|
||||
* then attacking with it. <br>
|
||||
* The last case would be an 'Ability-Assisted Attack'
|
||||
*/
|
||||
BOTH
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package net.Indyuce.mmoitems.gui.edition;
|
||||
|
||||
import io.lumine.mythic.lib.damage.DamageType;
|
||||
import io.lumine.mythic.lib.skill.handler.SkillHandler;
|
||||
import io.lumine.mythic.lib.skill.trigger.TriggerType;
|
||||
import net.Indyuce.mmoitems.ItemStats;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
@ -22,6 +24,8 @@ import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
@ -64,16 +68,14 @@ public class AbilityEdition extends EditionInventory {
|
||||
abilityItemMeta.setLore(abilityItemLore);
|
||||
abilityItem.setItemMeta(abilityItemMeta);
|
||||
|
||||
TriggerType castMode = null;
|
||||
if (ability != null) {
|
||||
String castModeConfigString = getEditedSection().getString("ability." + configKey + ".mode");
|
||||
String castModeFormat = castModeConfigString == null ? ""
|
||||
: castModeConfigString.toUpperCase().replace(" ", "_").replace("-", "_").replaceAll("[^A-Z0-9_]", "");
|
||||
TriggerType castMode;
|
||||
try {
|
||||
castMode = TriggerType.valueOf(castModeFormat);
|
||||
} catch (RuntimeException exception) {
|
||||
castMode = null;
|
||||
}
|
||||
} catch (RuntimeException ignored) { }
|
||||
|
||||
ItemStack castModeItem = new ItemStack(Material.ARMOR_STAND);
|
||||
ItemMeta castModeItemMeta = castModeItem.getItemMeta();
|
||||
@ -96,6 +98,8 @@ public class AbilityEdition extends EditionInventory {
|
||||
if (ability != null) {
|
||||
ConfigurationSection section = getEditedSection().getConfigurationSection("ability." + configKey);
|
||||
for (String modifier : ability.getHandler().getModifiers()) {
|
||||
if (!sensibleModifier(modifier, castMode)) { continue; }
|
||||
|
||||
ItemStack modifierItem = VersionMaterial.GRAY_DYE.toItem();
|
||||
ItemMeta modifierItemMeta = modifierItem.getItemMeta();
|
||||
modifierItemMeta.setDisplayName(ChatColor.GREEN + MMOUtils.caseOnWords(modifier.toLowerCase().replace("-", " ")));
|
||||
@ -105,9 +109,32 @@ public class AbilityEdition extends EditionInventory {
|
||||
modifierItemLore.add("");
|
||||
|
||||
try {
|
||||
|
||||
// Current Value Yeah
|
||||
NumericStatFormula heuh = new NumericStatFormula(section.get(modifier));
|
||||
String currentValue = heuh.toString();
|
||||
if (SkillHandler.SKMOD_DAMAGE_TYPE.equals(modifier)) {
|
||||
double dam = heuh.getBase();
|
||||
boolean orMode = dam < 0;
|
||||
if (orMode) { dam *= -1; }
|
||||
|
||||
// Parse display
|
||||
ArrayList<DamageType> whitelist = DamageType.getWhitelist(dam);
|
||||
ArrayList<DamageType> blacklist = DamageType.getBlacklist(dam);
|
||||
|
||||
// I guess append
|
||||
StringBuilder builder = new StringBuilder(orMode ? "OR" : "");
|
||||
for (DamageType white : whitelist) { if (builder.length() > 0) { builder.append(" "); } builder.append(white); }
|
||||
for (DamageType black : blacklist) { if (builder.length() > 0) { builder.append(" "); } builder.append("!").append(black); }
|
||||
|
||||
// Build Input
|
||||
currentValue = builder.toString() + " \u00a78(\u00a79" + heuh.toString() + "\u00a78)";
|
||||
}
|
||||
|
||||
modifierItemLore.add(ChatColor.GRAY + "Current Value: " + ChatColor.GOLD
|
||||
+ (section.contains(modifier) ? new NumericStatFormula(section.get(modifier)).toString()
|
||||
+ (section.contains(modifier) ? currentValue
|
||||
: MODIFIER_FORMAT.format(ability.getDefaultModifier(modifier))));
|
||||
|
||||
} catch (IllegalArgumentException exception) {
|
||||
modifierItemLore.add(ChatColor.GRAY + "Could not read value. Using default");
|
||||
}
|
||||
@ -146,6 +173,29 @@ public class AbilityEdition extends EditionInventory {
|
||||
return inv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some modifiers, like Timer or Damage Type Restriction,
|
||||
* only make sense if used in the triggers where they are
|
||||
* supported.
|
||||
*
|
||||
* @param modifier Modifier in question
|
||||
* @param trigger Trigger in question
|
||||
*
|
||||
* @return If this modifier makes sense for this trigger
|
||||
*/
|
||||
boolean sensibleModifier(@NotNull String modifier, @Nullable TriggerType trigger) {
|
||||
|
||||
// Missing cast mode might as well show all modifiers
|
||||
if (trigger == null) { return true; }
|
||||
|
||||
// These modifiers only work with the specific trigger
|
||||
if (modifier.equals(SkillHandler.SKMOD_DAMAGE_TYPE)) { return trigger.equals(TriggerType.ATTACK); }
|
||||
if (modifier.equals(SkillHandler.SKMOD_TIMER)) { return trigger.equals(TriggerType.TIMER); }
|
||||
|
||||
// Modifier is compatible with any trigger by default
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void whenClicked(InventoryClickEvent event) {
|
||||
ItemStack item = event.getCurrentItem();
|
||||
|
@ -7,6 +7,7 @@ import net.Indyuce.mmoitems.api.ConfigFile;
|
||||
import net.Indyuce.mmoitems.api.ReforgeOptions;
|
||||
import net.Indyuce.mmoitems.api.item.util.ConfigItem;
|
||||
import net.Indyuce.mmoitems.api.item.util.ConfigItems;
|
||||
import net.Indyuce.mmoitems.api.util.DamageTypeRestrictionSettings;
|
||||
import net.Indyuce.mmoitems.api.util.NumericStatFormula;
|
||||
import net.Indyuce.mmoitems.api.util.message.Message;
|
||||
import net.Indyuce.mmoitems.stat.GemUpgradeScaling;
|
||||
@ -36,6 +37,7 @@ public class ConfigManager implements Reloadable {
|
||||
|
||||
// cached config files
|
||||
private ConfigFile loreFormat, stats, dynLore;
|
||||
private FileConfiguration abilities;
|
||||
|
||||
// Language
|
||||
private final Map<TriggerType, String> triggerTypeNames = new HashMap<>();
|
||||
@ -48,6 +50,8 @@ public class ConfigManager implements Reloadable {
|
||||
public NumericStatFormula defaultItemCapacity;
|
||||
public ReforgeOptions revisionOptions, gemRevisionOptions, phatLootsOptions;
|
||||
public final List<String> opStats = new ArrayList<>();
|
||||
public boolean abilitiesBypassEncumbering, disableOffhandAbilities;
|
||||
public DamageTypeRestrictionSettings damageTypeRestrictionSettings;
|
||||
|
||||
public ConfigManager() {
|
||||
mkdir("layouts");
|
||||
@ -144,7 +148,7 @@ public class ConfigManager implements Reloadable {
|
||||
|
||||
// Trigger types
|
||||
triggerTypeNames.clear();
|
||||
final FileConfiguration abilities = new ConfigFile("/language", "abilities").getConfig();
|
||||
abilities = new ConfigFile("/language", "abilities").getConfig();
|
||||
for (TriggerType type : TriggerType.values())
|
||||
triggerTypeNames.put(type, abilities.getString("cast-mode." + type.getLowerCaseId(), type.getName()));
|
||||
}
|
||||
@ -155,6 +159,7 @@ public class ConfigManager implements Reloadable {
|
||||
loreFormat = new ConfigFile("/language", "lore-format");
|
||||
stats = new ConfigFile("/language", "stats");
|
||||
dynLore = new ConfigFile("/language", "dynamic-lore");
|
||||
abilities = new ConfigFile("/language", "abilities").getConfig();
|
||||
|
||||
loadTranslations();
|
||||
|
||||
@ -173,6 +178,8 @@ public class ConfigManager implements Reloadable {
|
||||
keepSoulboundOnDeath = MMOItems.plugin.getConfig().getBoolean("soulbound.keep-on-death");
|
||||
rerollOnItemUpdate = MMOItems.plugin.getConfig().getBoolean("item-revision.reroll-when-updated");
|
||||
levelSpread = MMOItems.plugin.getConfig().getDouble("item-level-spread");
|
||||
abilitiesBypassEncumbering = MMOItems.plugin.getConfig().getBoolean("abilities-bypass-encumbering", !MMOItems.plugin.getConfig().getBoolean("two-handed-item-restriction", true));
|
||||
disableOffhandAbilities = MMOItems.plugin.getConfig().getBoolean("disable-abilities-in-offhand", false);
|
||||
|
||||
opStatsEnabled = MMOItems.plugin.getConfig().getBoolean("op-item-stats.enabled");
|
||||
opStats.clear();
|
||||
@ -187,6 +194,8 @@ public class ConfigManager implements Reloadable {
|
||||
gemRevisionOptions = gemKeepData != null ? new ReforgeOptions(gemKeepData) : new ReforgeOptions(false, false, false, false, false, false, false, true);
|
||||
phatLootsOptions = phatLoots != null ? new ReforgeOptions(phatLoots) : new ReforgeOptions(false, false, false, false, false, false, false, true);
|
||||
|
||||
damageTypeRestrictionSettings = new DamageTypeRestrictionSettings(abilities.getConfigurationSection("damage-type-restriction"));
|
||||
|
||||
List<String> exemptedPhatLoots = MMOItems.plugin.getConfig().getStringList("item-revision.disable-phat-loot");
|
||||
for (String epl : exemptedPhatLoots)
|
||||
phatLootsOptions.addToBlacklist(epl);
|
||||
|
@ -5,8 +5,15 @@ import io.lumine.mythic.lib.MythicLib;
|
||||
import io.lumine.mythic.lib.api.item.ItemTag;
|
||||
import io.lumine.mythic.lib.api.item.SupportedNBTTagValues;
|
||||
import io.lumine.mythic.lib.api.util.AltChar;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackCategory;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import io.lumine.mythic.lib.damage.DamageType;
|
||||
import io.lumine.mythic.lib.skill.handler.SkillHandler;
|
||||
import io.lumine.mythic.lib.skill.trigger.TriggerType;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.util.DamageTypeRestrictionSettings;
|
||||
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
|
||||
import net.Indyuce.mmoitems.util.MMOUtils;
|
||||
import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
|
||||
@ -56,16 +63,24 @@ public class Abilities extends ItemStat<RandomAbilityListData, AbilityListData>
|
||||
//Modify Lore
|
||||
List<String> abilityLore = new ArrayList<>();
|
||||
boolean splitter = !MMOItems.plugin.getLanguage().abilitySplitter.equals("");
|
||||
DamageTypeRestrictionSettings settings = MMOItems.plugin.getLanguage().damageTypeRestrictionSettings;
|
||||
|
||||
String modifierFormat = ItemStat.translate("ability-modifier"), abilityFormat = ItemStat.translate("ability-format");
|
||||
|
||||
data.getAbilities().forEach(ability -> {
|
||||
abilityLore.add(abilityFormat.replace("{trigger}", MMOItems.plugin.getLanguage().getCastingModeName(ability.getTrigger())).replace("{ability}", ability.getAbility().getName()));
|
||||
|
||||
// Replace name of trigger, as well as name of ability
|
||||
String triggerDisplayName = settings.getTriggerDisplayName(ability.getTrigger(), ability.getModifier(SkillHandler.SKMOD_DAMAGE_TYPE));
|
||||
abilityLore.add(abilityFormat.replace("{trigger}", triggerDisplayName).replace("{ability}", ability.getAbility().getName()));
|
||||
|
||||
for (String modifier : ability.getModifiers()) {
|
||||
|
||||
// Damage Type Modifier does not display in lore
|
||||
if (modifier.equals(SkillHandler.SKMOD_DAMAGE_TYPE)) { continue; }
|
||||
|
||||
item.getLore().registerPlaceholder("ability_" + ability.getAbility().getHandler().getId().toLowerCase() + "_" + modifier,
|
||||
MythicLib.plugin.getMMOConfig().decimal.format(ability.getModifier(modifier)));
|
||||
abilityLore.add(modifierFormat.replace("{modifier}", ability.getAbility().getModifierName(modifier)).replace("{value}",
|
||||
abilityLore.add(modifierFormat.replace("{modifier}", settings.damageColors(ability.getAbility().getModifierName(modifier))).replace("{value}",
|
||||
MythicLib.plugin.getMMOConfig().decimal.format(ability.getModifier(modifier))));
|
||||
}
|
||||
|
||||
@ -133,8 +148,65 @@ public class Abilities extends ItemStat<RandomAbilityListData, AbilityListData>
|
||||
+ ChatColor.GRAY + ".");
|
||||
return;
|
||||
}
|
||||
String number = message;
|
||||
|
||||
new NumericStatFormula(message).fillConfigurationSection(inv.getEditedSection(), "ability." + configKey + "." + edited,
|
||||
// If we are editing the damage types and the provided value is not a number already
|
||||
if (SkillHandler.SKMOD_DAMAGE_TYPE.equals(edited) && !SilentNumbers.IntTryParse(number)) {
|
||||
|
||||
// Might come in handy....
|
||||
FriendlyFeedbackProvider ffp = new FriendlyFeedbackProvider(FFPMMOItems.get());
|
||||
ffp.activatePrefix(true, "Edition");
|
||||
boolean failure = false;
|
||||
|
||||
// Or Mode
|
||||
boolean orMode = message.startsWith("OR ");
|
||||
if (orMode) { message = message.substring("OR ".length()); }
|
||||
|
||||
// Build arrays
|
||||
ArrayList<DamageType> whitelisted = new ArrayList<>();
|
||||
ArrayList<DamageType> blacklisted = new ArrayList<>();
|
||||
|
||||
// Split by spaces
|
||||
String[] typesSplit = message.split(" ");
|
||||
for (String ty : typesSplit) {
|
||||
|
||||
// Crop blacklist
|
||||
boolean blacklist = false;
|
||||
String observed = ty.toUpperCase().replace("-", "_").replace(" ", "_");
|
||||
if (observed.startsWith("!")) { observed = observed.substring(1); blacklist = true; }
|
||||
|
||||
// Identify Damage Type
|
||||
try {
|
||||
|
||||
// Un-parse
|
||||
DamageType damageType = DamageType.valueOf(observed);
|
||||
|
||||
// Add to the lists
|
||||
if (blacklist) { blacklisted.add(damageType); } else { whitelisted.add(damageType); }
|
||||
|
||||
// Mention
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
|
||||
// no
|
||||
failure = true;
|
||||
|
||||
ffp.log(FriendlyFeedbackCategory.ERROR, "Unknown damage type '$r{0}$b' in '$u{1}$b'. ", observed, ty);
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel
|
||||
if (failure) {
|
||||
|
||||
// Errors
|
||||
ffp.sendAllTo(inv.getPlayer());
|
||||
throw new IllegalArgumentException("$bInvalid input! Please specify damage types to require or blacklist, for example: '$eMAGIC WEAPON !SKILL$b' or '$eOR PHYSICAL PROJECTILE MINION !MAGIC$b'. ");
|
||||
}
|
||||
|
||||
// Bake number
|
||||
number = String.valueOf(DamageType.encodeDamageTypeMatch(whitelisted, blacklisted, orMode));
|
||||
}
|
||||
|
||||
new NumericStatFormula(number).fillConfigurationSection(inv.getEditedSection(), "ability." + configKey + "." + edited,
|
||||
FormulaSaveOption.NONE);
|
||||
inv.registerTemplateEdition();
|
||||
inv.getPlayer().sendMessage(MMOItems.plugin.getPrefix() + ChatColor.GOLD + MMOUtils.caseOnWords(edited.replace("-", " ")) + ChatColor.GRAY
|
||||
|
@ -21,3 +21,42 @@ cast-mode:
|
||||
trident-hit: Trident Hit
|
||||
damaged-by-entity: Damaged By Entity
|
||||
shift-right-click: Shift Right Click
|
||||
|
||||
# For the On Attack trigger there exists the
|
||||
# Modifier 'Damage Type Restriction' that supports
|
||||
# advanced translation logic
|
||||
damage-type-restrictions:
|
||||
|
||||
## -----
|
||||
## Translate the damage types
|
||||
damage-type-translations:
|
||||
MAGIC: "Magic"
|
||||
PHYSICAL: "Melee"
|
||||
PROJECTILE: "Ranged"
|
||||
WEAPON: "Weapon"
|
||||
SKILL: "Skill"
|
||||
UNARMED: "Unarmed"
|
||||
ON_HIT: "Reaction"
|
||||
MINION: "Minion"
|
||||
DOT: "Lingering"
|
||||
|
||||
## -----
|
||||
# Translate the types of attack
|
||||
attack-type-translations:
|
||||
WEAPON: "Attack"
|
||||
SKILL: "Ability Hit"
|
||||
NEITHER: "Damage"
|
||||
BOTH: "Ability-Assisted Attack"
|
||||
|
||||
## -----
|
||||
## Change the color of scalings
|
||||
# damage-type-colors:
|
||||
# MAGIC: "&9"
|
||||
# PHYSICAL: "&8"
|
||||
# WEAPON: "&7"
|
||||
# SKILL: "&f"
|
||||
# PROJECTILE: "&a"
|
||||
# UNARMED: "&e"
|
||||
# ON_HIT: "&0"
|
||||
# MINION: "&d"
|
||||
# DOT: "&3"
|
Loading…
Reference in New Issue
Block a user