Combat commit 1

This commit is contained in:
Jules 2023-01-22 21:59:06 +01:00
parent fa6809b483
commit 30bf4b0e99
26 changed files with 570 additions and 226 deletions

View File

@ -2,15 +2,13 @@ package net.Indyuce.mmocore;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.metrics.bukkit.Metrics;
import io.lumine.mythic.lib.version.SpigotPlugin;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.command.api.ToggleableCommand;
import net.Indyuce.mmocore.manager.social.PartyManager;
import net.Indyuce.mmocore.api.ConfigFile;
import net.Indyuce.mmocore.manager.ActionBarManager;
import net.Indyuce.mmocore.api.player.attribute.AttributeModifier;
import net.Indyuce.mmocore.api.player.profess.resource.PlayerResource;
import net.Indyuce.mmocore.command.*;
import net.Indyuce.mmocore.command.MMOCoreCommandTreeRoot;
import net.Indyuce.mmocore.command.api.ToggleableCommand;
import net.Indyuce.mmocore.comp.citizens.CitizenInteractEventListener;
import net.Indyuce.mmocore.comp.citizens.CitizensMMOLoader;
import net.Indyuce.mmocore.comp.mythicmobs.MythicHook;
@ -22,10 +20,12 @@ import net.Indyuce.mmocore.comp.region.DefaultRegionHandler;
import net.Indyuce.mmocore.comp.region.RegionHandler;
import net.Indyuce.mmocore.comp.region.WorldGuardMMOLoader;
import net.Indyuce.mmocore.comp.region.WorldGuardRegionHandler;
import net.Indyuce.mmocore.comp.region.pvpmode.PvPModeListener;
import net.Indyuce.mmocore.comp.vault.VaultEconomy;
import net.Indyuce.mmocore.comp.vault.VaultMMOLoader;
import net.Indyuce.mmocore.guild.GuildModule;
import net.Indyuce.mmocore.guild.GuildModuleType;
import net.Indyuce.mmocore.guild.GuildRelationHandler;
import net.Indyuce.mmocore.guild.provided.Guild;
import net.Indyuce.mmocore.guild.provided.MMOCoreGuildModule;
import net.Indyuce.mmocore.manager.*;
@ -34,28 +34,25 @@ import net.Indyuce.mmocore.manager.data.mysql.MySQLDataProvider;
import net.Indyuce.mmocore.manager.data.yaml.YAMLDataProvider;
import net.Indyuce.mmocore.manager.profession.*;
import net.Indyuce.mmocore.manager.social.BoosterManager;
import net.Indyuce.mmocore.manager.social.PartyManager;
import net.Indyuce.mmocore.manager.social.RequestManager;
import net.Indyuce.mmocore.party.MMOCoreTargetRestriction;
import net.Indyuce.mmocore.party.PartyModule;
import net.Indyuce.mmocore.party.PartyModuleType;
import net.Indyuce.mmocore.party.PartyRelationHandler;
import net.Indyuce.mmocore.party.provided.MMOCorePartyModule;
import net.Indyuce.mmocore.skill.cast.SkillCastingMode;
import net.Indyuce.mmocore.script.mechanic.ExperienceMechanic;
import net.Indyuce.mmocore.script.mechanic.StaminaMechanic;
import net.Indyuce.mmocore.script.mechanic.ManaMechanic;
import net.Indyuce.mmocore.script.mechanic.StaminaMechanic;
import net.Indyuce.mmocore.script.mechanic.StelliumMechanic;
import net.Indyuce.mmocore.skill.cast.SkillCastingMode;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import io.lumine.mythic.lib.metrics.bukkit.Metrics;
import org.bukkit.command.CommandMap;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.lang.reflect.Field;
import java.util.logging.Level;
public class MMOCore extends JavaPlugin {
@ -99,7 +96,6 @@ public class MMOCore extends JavaPlugin {
@NotNull
public PartyModule partyModule;
public GuildModule guildModule;
public boolean shouldDebugSQL = false;
public MMOCore() {
plugin = this;
@ -109,7 +105,8 @@ public class MMOCore extends JavaPlugin {
public void onLoad() {
// Register MMOCore-specific objects
MythicLib.plugin.getEntities().registerRestriction(new MMOCoreTargetRestriction());
MythicLib.plugin.getEntities().registerRelationHandler(new PartyRelationHandler());
MythicLib.plugin.getEntities().registerRelationHandler(new GuildRelationHandler());
MythicLib.plugin.getModifiers().registerModifierType("attribute", configObject -> new AttributeModifier(configObject));
// Custom scripts
@ -146,7 +143,6 @@ public class MMOCore extends JavaPlugin {
if (getConfig().isConfigurationSection("mysql") && getConfig().getBoolean("mysql.enabled"))
dataProvider = new MySQLDataProvider(getConfig());
shouldDebugSQL = getConfig().getBoolean("mysql.debug");
if (getConfig().isConfigurationSection("default-playerdata"))
dataProvider.getDataManager().loadDefaultData(getConfig().getConfigurationSection("default-playerdata"));
@ -165,6 +161,8 @@ public class MMOCore extends JavaPlugin {
if (Bukkit.getPluginManager().getPlugin("WorldGuard") != null) {
regionHandler = new WorldGuardRegionHandler();
if (MythicLib.plugin.getConfig().getBoolean("pvp_mode.enabled"))
Bukkit.getPluginManager().registerEvents(new PvPModeListener(), this);
getLogger().log(Level.INFO, "Hooked onto WorldGuard");
}
@ -267,7 +265,7 @@ public class MMOCore extends JavaPlugin {
// Save player data
for (PlayerData data : PlayerData.getAll())
if (data.isFullyLoaded())
dataProvider.getDataManager().saveData(data);
dataProvider.getDataManager().saveData(data, false);
// Save guild info
for (Guild guild : dataProvider.getGuildManager().getAll())
@ -290,7 +288,7 @@ public class MMOCore extends JavaPlugin {
for (PlayerData data : PlayerData.getAll())
if (data.isFullyLoaded()) {
data.close();
dataProvider.getDataManager().saveData(data);
dataProvider.getDataManager().saveData(data, true);
}
// Save guild info
@ -373,7 +371,7 @@ public class MMOCore extends JavaPlugin {
}
public static void sqlDebug(String s) {
if (!MMOCore.plugin.shouldDebugSQL) return;
if (!MMOCore.plugin.configManager.sqlDebug) return;
MMOCore.plugin.getLogger().warning("- [SQL Debug] " + s);
}
}

View File

@ -1,49 +0,0 @@
package net.Indyuce.mmocore.api.player;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.event.PlayerCombatEvent;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitRunnable;
public class CombatRunnable extends BukkitRunnable {
private final PlayerData player;
private long lastHit = System.currentTimeMillis();
private boolean open = true;
public CombatRunnable(PlayerData player) {
this.player = player;
if (player.isOnline()) {
MMOCore.plugin.configManager.getSimpleMessage("now-in-combat").send(player.getPlayer());
Bukkit.getPluginManager().callEvent(new PlayerCombatEvent(player, true));
runTaskTimer(MMOCore.plugin, 20, 20);
}
}
public void update() {
lastHit = System.currentTimeMillis();
}
@Override
public void run() {
if (!player.isOnline()) {
close();
return;
}
if (lastHit + MMOCore.plugin.configManager.combatLogTimer < System.currentTimeMillis()) {
Bukkit.getPluginManager().callEvent(new PlayerCombatEvent(player, false));
MMOCore.plugin.configManager.getSimpleMessage("leave-combat").send(player.getPlayer());
close();
}
}
private void close() {
Validate.isTrue(open, "Combat runnable has already been closed");
player.combat = null;
cancel();
open = false;
}
}

View File

@ -6,11 +6,11 @@ import javax.inject.Provider;
import java.util.Objects;
/**
* Used by MMOCore when it has to store the last time
* a player did some action.
* <p>
* This also features a time out function which can
* be used for cooldowns
* Used by MMOCore when it has to store the last time a player
* did some action. This also features a time out function which
* can be used for cooldowns.
*
* @deprecated Merge with {@link io.lumine.mythic.lib.player.cooldown.CooldownMap}
*/
public enum PlayerActivity {
USE_WAYPOINT(() -> 5 * 1000L),
@ -21,7 +21,9 @@ public enum PlayerActivity {
LOOT_CHEST_SPAWN(() -> MMOCore.plugin.configManager.lootChestPlayerCooldown),
CAST_SKILL(() -> MMOCore.plugin.configManager.globalSkillCooldown);
CAST_SKILL(() -> MMOCore.plugin.configManager.globalSkillCooldown),
;
private final Provider<Long> timeout;

View File

@ -35,6 +35,7 @@ import net.Indyuce.mmocore.party.AbstractParty;
import net.Indyuce.mmocore.party.provided.MMOCorePartyModule;
import net.Indyuce.mmocore.party.provided.Party;
import net.Indyuce.mmocore.player.ClassDataContainer;
import net.Indyuce.mmocore.player.CombatHandler;
import net.Indyuce.mmocore.player.Unlockable;
import net.Indyuce.mmocore.skill.ClassSkill;
import net.Indyuce.mmocore.skill.RegisteredSkill;
@ -95,6 +96,7 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
private final PlayerAttributes attributes = new PlayerAttributes(this);
private final Map<String, SavedClassInformation> classSlots = new HashMap<>();
private final Map<PlayerActivity, Long> lastActivity = new HashMap<>();
private final CombatHandler combat = new CombatHandler(this);
/**
* Cached for easier access. Amount of points spent in each skill tree.
@ -125,8 +127,7 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
private final Map<String, Integer> tableItemClaims = new HashMap<>();
// NON-FINAL player data stuff made public to facilitate field change
public boolean noCooldown, statsLoaded, pvpMode;
public CombatRunnable combat;
public boolean noCooldown, statsLoaded;
/**
* Player data is stored in the data map before it's actually fully loaded
@ -370,6 +371,9 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
((Party) party).removeMember(this);
}
// Close combat handler
combat.close();
// Close quest data
questData.close();
@ -748,9 +752,9 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
final double r = Math.sin((double) t / warpTime * Math.PI);
for (double j = 0; j < Math.PI * 2; j += Math.PI / 4)
getPlayer().getLocation().getWorld().spawnParticle(Particle.REDSTONE, getPlayer().getLocation().add(
Math.cos((double) 5 * t / warpTime + j) * r,
(double) 2 * t / warpTime,
Math.sin((double) 5 * t / warpTime + j) * r),
Math.cos((double) 5 * t / warpTime + j) * r,
(double) 2 * t / warpTime,
Math.sin((double) 5 * t / warpTime + j) * r),
1, new Particle.DustOptions(Color.PURPLE, 1.25f));
}
}.runTaskTimer(MMOCore.plugin, 0, 1);
@ -1175,8 +1179,13 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
return boundPassiveSkills;
}
@NotNull
public CombatHandler getCombat() {
return combat;
}
public boolean isInCombat() {
return combat != null;
return getCombat().isInCombat();
}
/**
@ -1184,7 +1193,7 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
* checks if they could potentially upgrade to one of these
*
* @return If the player can change its current class to
* a subclass
* a subclass
*/
@Deprecated
public boolean canChooseSubclass() {
@ -1198,11 +1207,9 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
* Everytime a player does a combat action, like taking
* or dealing damage to an entity, this method is called.
*/
@Deprecated
public void updateCombat() {
if (isInCombat())
combat.update();
else
combat = new CombatRunnable(this);
getCombat().update();
}
@Override

View File

@ -1,76 +1,45 @@
package net.Indyuce.mmocore.command;
import io.lumine.mythic.lib.MythicLib;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.event.MMOCommandEvent;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.social.Request;
import net.Indyuce.mmocore.command.api.RegisteredCommand;
import net.Indyuce.mmocore.command.api.ToggleableCommand;
import net.Indyuce.mmocore.guild.provided.GuildInvite;
import net.Indyuce.mmocore.manager.InventoryManager;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public class PvpModeCommand extends RegisteredCommand {
public PvpModeCommand(ConfigurationSection config) {
super(config, ToggleableCommand.PVP_MODE);
}
public static final String COOLDOWN_KEY = "PvpMode";
@Override
public boolean execute(CommandSender sender, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "This command is for players only.");
return false;
}
if (!sender.hasPermission("mmocore.pvpmode")) {
MMOCore.plugin.configManager.getSimpleMessage("not-enough-perms").send((Player) sender);
return false;
}
final PlayerData playerData = PlayerData.get(((Player) sender).getUniqueId());
// Command cooldown
if (playerData.getCooldownMap().isOnCooldown(COOLDOWN_KEY)) {
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.cooldown", "remaining", MythicLib.plugin.getMMOConfig().decimal.format(playerData.getCooldownMap().getCooldown(COOLDOWN_KEY))).send((Player) sender);
return true;
}
PlayerData data = PlayerData.get((OfflinePlayer) sender);
MMOCommandEvent event = new MMOCommandEvent(data, "guild");
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) return true;
if (args.length > 1) {
final @Nullable GuildInvite invite;
if (args.length > 1)
// Search by request ID
try {
final UUID uuid = UUID.fromString(args[1]);
final Request req = MMOCore.plugin.requestManager.getRequest(uuid);
Validate.isTrue(!req.isTimedOut() && req instanceof GuildInvite);
invite = (GuildInvite) req;
Validate.isTrue(MMOCore.plugin.dataProvider.getGuildManager().isRegistered(invite.getGuild()));
} catch (Exception exception) {
return true;
}
// Search by target player
else
invite = MMOCore.plugin.requestManager.findRequest(data, GuildInvite.class);
// No invite found with given identifier/target player
if (invite == null)
return true;
if (args[0].equalsIgnoreCase("accept"))
invite.accept();
if (args[0].equalsIgnoreCase("deny"))
invite.deny();
return true;
}
if (data.inGuild())
InventoryManager.GUILD_VIEW.newInventory(data).open();
else
InventoryManager.GUILD_CREATION.newInventory(data).open();
playerData.getCombat().setPvpMode(!playerData.getCombat().isInPvpMode());
playerData.getCooldownMap().applyCooldown(COOLDOWN_KEY, playerData.getCombat().isInPvpMode() ? MMOCore.plugin.configManager.pvpModeToggleOnCooldown : MMOCore.plugin.configManager.pvpModeToggleOffCooldown);
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.toggle-" + (playerData.getCombat().isInPvpMode() ? "on" : "off")).send((Player) sender);
return true;
}
}

View File

@ -64,14 +64,10 @@ public class RPGPlaceholders extends PlaceholderExpansion {
String id = identifier.substring(12);
RegisteredSkill skill = Objects.requireNonNull(MMOCore.plugin.skillManager.getSkill(id), "Could not find skill with ID '" + id + "'");
return String.valueOf(playerData.getSkillLevel(skill));
}
else if (identifier.equals("level_percent")) {
} else if (identifier.equals("level_percent")) {
double current = playerData.getExperience(), next = playerData.getLevelUpExperience();
return MythicLib.plugin.getMMOConfig().decimal.format(current / next * 100);
}
else if (identifier.equals("health"))
} else if (identifier.equals("health"))
return StatManager.format("MAX_HEALTH", player.getPlayer().getHealth());
else if (identifier.equals("max_health"))
@ -83,9 +79,7 @@ public class RPGPlaceholders extends PlaceholderExpansion {
for (double j = 1; j < 20; j++)
format.append(ratio >= j ? ChatColor.RED : ratio >= j - .5 ? ChatColor.DARK_RED : ChatColor.DARK_GRAY).append(AltChar.listSquare);
return format.toString();
}
else if (identifier.equals("class"))
} else if (identifier.equals("class"))
return playerData.getProfess().getName();
else if (identifier.startsWith("profession_percent_")) {
@ -94,21 +88,25 @@ public class RPGPlaceholders extends PlaceholderExpansion {
Profession profession = MMOCore.plugin.professionManager.get(name);
double current = professions.getExperience(profession), next = professions.getLevelUpExperience(profession);
return MythicLib.plugin.getMMOConfig().decimal.format(current / next * 100);
}
else if (identifier.startsWith("is_casting")) {
} else if (identifier.equals("is_casting"))
return String.valueOf(playerData.isCasting());
} else if (identifier.startsWith("in_combat")) {
return String.valueOf(playerData.isInCombat());
}
else if (identifier.startsWith("bound_")) {
else if (identifier.equals("in_combat"))
return String.valueOf(playerData.isInCombat());
else if (identifier.startsWith("since_enter_combat"))
return playerData.isInCombat() ? MythicLib.plugin.getMMOConfig().decimal.format((System.currentTimeMillis() - playerData.getCombat().getFirstHit()) / 1000) : "-1";
else if (identifier.startsWith("since_last_hit"))
return playerData.isInCombat() ? MythicLib.plugin.getMMOConfig().decimal.format((System.currentTimeMillis() - playerData.getCombat().getLastHit()) / 1000) : "-1";
else if (identifier.startsWith("bound_")) {
int slot = Math.max(0, Integer.parseInt(identifier.substring(6)) - 1);
return playerData.hasSkillBound(slot) ? playerData.getBoundSkill(slot).getSkill().getName()
: MMOCore.plugin.configManager.noSkillBoundPlaceholder;
}
else if (identifier.startsWith("profession_experience_"))
} else if (identifier.startsWith("profession_experience_"))
return MythicLib.plugin.getMMOConfig().decimal.format(
playerData.getCollectionSkills().getExperience(identifier.substring(22).replace(" ", "-").replace("_", "-").toLowerCase()));
@ -150,9 +148,8 @@ public class RPGPlaceholders extends PlaceholderExpansion {
else if (identifier.equals("mana"))
return MythicLib.plugin.getMMOConfig().decimal.format(playerData.getMana());
else if (identifier.equals("mana_bar")) {
else if (identifier.equals("mana_bar"))
return playerData.getProfess().getManaDisplay().generateBar(playerData.getMana(), playerData.getStats().getStat("MAX_MANA"));
}
else if (identifier.startsWith("exp_multiplier_")) {
String format = identifier.substring(15).toLowerCase().replace("_", "-").replace(" ", "-");

View File

@ -0,0 +1,114 @@
package net.Indyuce.mmocore.comp.region.pvpmode;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldguard.LocalPlayer;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.flags.StateFlag;
import com.sk89q.worldguard.protection.flags.StateFlag.State;
import com.sk89q.worldguard.session.MoveType;
import com.sk89q.worldguard.session.Session;
import com.sk89q.worldguard.session.handler.FlagValueChangeHandler;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.comp.flags.CustomFlag;
import io.lumine.mythic.lib.comp.flags.WorldGuardFlags;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class PvPModeHandler extends FlagValueChangeHandler<State> {
@NotNull
private PlayerData playerData;
private long lastMessage;
private static final long MESSAGE_TIMEOUT = 3 * 1000;
public static final Factory FACTORY = new Factory() {
public final WorldGuardFlags wgFlags = Objects.requireNonNull(MythicLib.plugin.getFlags().getHandler(WorldGuardFlags.class), "Could not reach ML compatibility class for WG");
@Override
public PvPModeHandler create(Session session) {
return new PvPModeHandler(session, wgFlags.toWorldGuard(CustomFlag.PVP_MODE));
}
};
public PvPModeHandler(Session session, StateFlag flag) {
super(session, flag);
}
/**
* Triggered when WorldGuard initializes the value for the first time,
* on player login or world change for instance.
*/
@Override
protected void onInitialValue(LocalPlayer player, ApplicableRegionSet set, State value) {
try {
playerData = PlayerData.get(player.getUniqueId());
} catch (Exception exception) {
// Citizens.
}
}
/**
* Triggered when WorldGuard does not find a region setting the value of the flag.
* In that case, put PvP mode to its default setting that is OFF.
*/
@Override
protected boolean onAbsentValue(LocalPlayer player, Location from, Location to, ApplicableRegionSet toSet, State lastValue, MoveType moveType) {
return true;
}
/**
* Triggered when a player changes region and finds a new value for that flag.
* In that case, apply the new setting and display messages if needed.
*/
@Override
protected boolean onSetValue(LocalPlayer player, Location from, Location to, ApplicableRegionSet toSet, State currentValue, State lastValue, MoveType moveType) {
// Do nothing if pvpmode is disabled.
if (isInvalid() || !playerData.getCombat().isInPvpMode())
return true;
final boolean newPvpMode = toBoolean(currentValue);
final boolean lastPvpMode = toBoolean(lastValue);
if (!playerData.getCombat().canQuitPvpMode()) {
// Leaving a custom Pvp zone
if (!newPvpMode && lastPvpMode && canSendMessage()) {
lastMessage = System.currentTimeMillis();
final double remaining = (playerData.getCombat().getLastHit() + MMOCore.plugin.configManager.pvpModeCombatTimeout * 1000 - System.currentTimeMillis()) / 1000;
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.leave", "remaining", MythicLib.plugin.getMMOConfig().decimal.format(remaining)).send(playerData.getPlayer());
}
} else if (newPvpMode && !lastPvpMode) {
// Apply invulnerability
playerData.getCombat().applyInvulnerability();
// Entering Pvp zone
if (canSendMessage()) {
lastMessage = System.currentTimeMillis();
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.enter", "time", MythicLib.plugin.getMMOConfig().decimal.format(MMOCore.plugin.configManager.pvpModeInvulnerability)).send(playerData.getPlayer());
}
}
return true;
}
private boolean isInvalid() {
return playerData == null;
}
private boolean toBoolean(@Nullable State state) {
return state == State.ALLOW;
}
private boolean canSendMessage() {
return System.currentTimeMillis() > lastMessage + MESSAGE_TIMEOUT;
}
}

View File

@ -0,0 +1,33 @@
package net.Indyuce.mmocore.comp.region.pvpmode;
import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.bukkit.protection.events.DisallowedPVPEvent;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.comp.flags.CustomFlag;
import net.Indyuce.mmocore.api.player.PlayerData;
import org.apache.commons.lang.Validate;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class PvPModeListener implements Listener {
public PvPModeListener() {
Validate.isTrue(WorldGuard.getInstance().getPlatform().getSessionManager().registerHandler(PvPModeHandler.FACTORY, null), "Could not register WG handler for PvP mode");
}
@EventHandler(ignoreCancelled = true)
public void unblockPvp(DisallowedPVPEvent event) {
final PlayerData defender;
// Make sure both have PVP mode on
if (!PlayerData.get(event.getAttacker()).getCombat().isInPvpMode() || !(defender = PlayerData.get(event.getDefender())).getCombat().isInPvpMode())
return;
// If there are in a PVP zone
if (MythicLib.plugin.getFlags().isFlagAllowed(event.getDefender().getLocation(), CustomFlag.PVP_MODE) && !defender.getCombat().isInvulnerable())
event.setCancelled(true);
// If target cannot quit pvp zone yet
else if (!defender.getCombat().canQuitPvpMode())
event.setCancelled(true);
}
}

View File

@ -1,10 +1,14 @@
package net.Indyuce.mmocore.guild;
import io.lumine.mythic.lib.comp.interaction.relation.Relationship;
import net.Indyuce.mmocore.api.player.PlayerData;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
public interface GuildModule {
@Nullable
public AbstractGuild getGuild(PlayerData playerData);
public Relationship getRelationship(Player player, Player target);
}

View File

@ -0,0 +1,16 @@
package net.Indyuce.mmocore.guild;
import io.lumine.mythic.lib.comp.interaction.relation.Relationship;
import io.lumine.mythic.lib.comp.interaction.relation.RelationshipHandler;
import net.Indyuce.mmocore.MMOCore;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class GuildRelationHandler implements RelationshipHandler {
@NotNull
@Override
public Relationship getRelationship(Player player, Player target) {
return MMOCore.plugin.guildModule.getRelationship(player, target);
}
}

View File

@ -1,19 +0,0 @@
package net.Indyuce.mmocore.guild;
public enum RelationType {
/**
* In the same guild
*/
ALLY,
/**
* One of the two players has no guild
*/
NEUTRAL,
/**
*
*/
ENEMY;
}

View File

@ -1,12 +1,13 @@
package net.Indyuce.mmocore.guild.compat;
import com.massivecraft.factions.FPlayer;
import com.massivecraft.factions.FPlayers;
import com.massivecraft.factions.Faction;
import cc.javajobs.factionsbridge.FactionsBridge;
import cc.javajobs.factionsbridge.bridge.infrastructure.struct.FPlayer;
import cc.javajobs.factionsbridge.bridge.infrastructure.struct.Faction;
import cc.javajobs.factionsbridge.bridge.infrastructure.struct.FactionsAPI;
import io.lumine.mythic.lib.comp.interaction.relation.Relationship;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.guild.AbstractGuild;
import net.Indyuce.mmocore.guild.GuildModule;
import org.apache.commons.lang.NotImplementedException;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@ -16,11 +17,38 @@ public class FactionsGuildModule implements GuildModule {
@Override
public AbstractGuild getGuild(PlayerData playerData) {
FPlayer fPlayer = FPlayers.getInstance().getByPlayer(playerData.getPlayer());
if (fPlayer == null)
return null;
final FactionsAPI api = FactionsBridge.getFactionsAPI();
final Faction faction = api.getFaction(playerData.getPlayer());
return faction == null ? null : new CustomGuild(faction);
}
return fPlayer.hasFaction() ? new CustomGuild(fPlayer.getFaction()) : null;
@Override
public Relationship getRelationship(Player player, Player target) {
final FactionsAPI api = FactionsBridge.getFactionsAPI();
Faction faction = api.getFaction(player);
if (faction != null)
return adapt(faction.getRelationshipTo(api.getFPlayer(target)));
faction = api.getFaction(target);
if (faction != null)
return adapt(faction.getRelationshipTo(api.getFPlayer(player)));
return Relationship.GUILD_NEUTRAL;
}
private Relationship adapt(cc.javajobs.factionsbridge.bridge.infrastructure.struct.Relationship rel) {
switch (rel) {
case ENEMY:
return Relationship.GUILD_ENEMY;
case ALLY:
case TRUCE:
case MEMBER:
return Relationship.GUILD_ALLY;
case NONE:
default:
return Relationship.GUILD_NEUTRAL;
}
}
class CustomGuild implements AbstractGuild {
@ -34,7 +62,10 @@ public class FactionsGuildModule implements GuildModule {
@Override
public boolean hasMember(Player player) {
throw new NotImplementedException();
for (FPlayer member : faction.getMembers())
if (member.getUniqueId().equals(player.getUniqueId()))
return true;
return false;
}
}
}

View File

@ -1,5 +1,6 @@
package net.Indyuce.mmocore.guild.compat;
import io.lumine.mythic.lib.comp.interaction.relation.Relationship;
import me.glaremasters.guilds.Guilds;
import me.glaremasters.guilds.guild.Guild;
import net.Indyuce.mmocore.api.player.PlayerData;
@ -18,6 +19,19 @@ public class GuildsGuildModule implements GuildModule {
return guild == null ? null : new CustomGuild(guild);
}
@Override
public Relationship getRelationship(Player player, Player target) {
final Guild guild1 = Guilds.getApi().getGuild(player);
if (guild1 == null)
return Relationship.GUILD_NEUTRAL;
final Guild guild2 = Guilds.getApi().getGuild(target);
if (guild2 == null)
return Relationship.GUILD_NEUTRAL;
return guild1.getId().equals(guild2.getId()) || guild1.getAllies().contains(guild2.getId()) ? Relationship.GUILD_ALLY : Relationship.GUILD_ENEMY;
}
class CustomGuild implements AbstractGuild {
@NotNull

View File

@ -1,11 +1,13 @@
package net.Indyuce.mmocore.guild.compat;
import io.lumine.mythic.lib.comp.interaction.relation.Relationship;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.guild.AbstractGuild;
import net.Indyuce.mmocore.guild.GuildModule;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.kingdoms.constants.kingdom.Kingdom;
import org.kingdoms.constants.kingdom.model.KingdomRelation;
import org.kingdoms.constants.player.KingdomPlayer;
import org.kingdoms.main.Kingdoms;
@ -23,6 +25,44 @@ public class KingdomsXGuildModule implements GuildModule {
return kingdom == null ? null : new CustomGuild(kingdom);
}
@Override
public Relationship getRelationship(Player player, Player target) {
final KingdomPlayer player1 = Kingdoms.get().getDataHandlers().getKingdomPlayerManager().getData(player.getUniqueId());
if (player1 == null)
return null;
final Kingdom kingdom1 = player1.getKingdom();
if (kingdom1 == null)
return Relationship.GUILD_NEUTRAL;
final KingdomPlayer player2 = Kingdoms.get().getDataHandlers().getKingdomPlayerManager().getData(target.getUniqueId());
if (player2 == null)
return null;
final Kingdom kingdom2 = player2.getKingdom();
if (kingdom2 == null)
return Relationship.GUILD_NEUTRAL;
return adapt(kingdom1.getRelationWith(kingdom2));
}
private Relationship adapt(KingdomRelation rel) {
switch (rel) {
case ALLY:
case SELF:
return Relationship.GUILD_ALLY;
case ENEMY:
return Relationship.GUILD_ENEMY;
case NEUTRAL:
case TRUCE:
case NATION:
default:
return Relationship.GUILD_NEUTRAL;
}
}
class CustomGuild implements AbstractGuild {
@NotNull

View File

@ -1,6 +1,8 @@
package net.Indyuce.mmocore.guild.compat;
import io.lumine.mythic.lib.comp.interaction.relation.Relationship;
import me.ulrich.clans.data.ClanData;
import me.ulrich.clans.data.ClanRivalAlly;
import me.ulrich.clans.packets.interfaces.UClans;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.guild.AbstractGuild;
@ -10,6 +12,7 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.UUID;
public class UltimateClansGuildModule implements GuildModule {
private static final UClans API = (UClans) Bukkit.getPluginManager().getPlugin("UltimateCLans");
@ -19,6 +22,16 @@ public class UltimateClansGuildModule implements GuildModule {
return API.getPlayerAPI().hasClan(playerData.getUniqueId()) ? new CustomGuild(API.getClanAPI().getClan(API.getPlayerAPI().getClanID(playerData.getUniqueId()))) : null;
}
@Override
public Relationship getRelationship(Player player, Player target) {
if (!API.getPlayerAPI().hasClan(player.getUniqueId()) || !API.getPlayerAPI().hasClan(target.getUniqueId()))
return Relationship.GUILD_NEUTRAL;
final ClanRivalAlly clan1 = API.getClanAPI().getClan(API.getPlayerAPI().getClanID(player.getUniqueId())).getRivalAlly();
final UUID clanId2 = API.getPlayerAPI().getClanID(target.getUniqueId());
return clan1.getAlly().contains(clanId2) ? Relationship.GUILD_ALLY : clan1.getRival().contains(clanId2) ? Relationship.GUILD_ENEMY : Relationship.GUILD_NEUTRAL;
}
class CustomGuild implements AbstractGuild {
@NotNull

View File

@ -1,13 +1,14 @@
package net.Indyuce.mmocore.guild.provided;
import io.lumine.mythic.lib.comp.interaction.relation.Relationship;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.social.Request;
import net.Indyuce.mmocore.gui.api.PluginInventory;
import net.Indyuce.mmocore.gui.social.guild.EditableGuildView;
import net.Indyuce.mmocore.guild.AbstractGuild;
import net.Indyuce.mmocore.manager.InventoryManager;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.player.social.Request;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;

View File

@ -1,8 +1,10 @@
package net.Indyuce.mmocore.guild.provided;
import io.lumine.mythic.lib.comp.interaction.relation.Relationship;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.guild.AbstractGuild;
import net.Indyuce.mmocore.guild.GuildModule;
import org.bukkit.entity.Player;
public class MMOCoreGuildModule implements GuildModule {
@ -10,4 +12,17 @@ public class MMOCoreGuildModule implements GuildModule {
public AbstractGuild getGuild(PlayerData playerData) {
return playerData.getGuild();
}
@Override
public Relationship getRelationship(Player player, Player target) {
final Guild guild1 = PlayerData.get(player).getGuild();
if (guild1 == null)
return Relationship.GUILD_NEUTRAL;
final Guild guild2 = PlayerData.get(target).getGuild();
if (guild2 == null)
return Relationship.GUILD_NEUTRAL;
return guild1.equals(guild2) ? Relationship.GUILD_ALLY : Relationship.GUILD_ENEMY;
}
}

View File

@ -10,6 +10,7 @@ import net.Indyuce.mmocore.api.util.input.PlayerInput;
import net.Indyuce.mmocore.api.util.input.PlayerInput.InputType;
import net.Indyuce.mmocore.command.api.CommandVerbose;
import org.bukkit.ChatColor;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityDamageEvent;
@ -26,11 +27,11 @@ import java.util.logging.Level;
public class ConfigManager {
public final CommandVerbose commandVerbose = new CommandVerbose();
public boolean overrideVanillaExp, canCreativeCast, passiveSkillNeedBound, cobbleGeneratorXP, saveDefaultClassInfo, attributesAsClassInfo, splitProfessionExp, disableQuestBossBar;
public boolean overrideVanillaExp, canCreativeCast, passiveSkillNeedBound, cobbleGeneratorXP, saveDefaultClassInfo, attributesAsClassInfo, splitProfessionExp, disableQuestBossBar, pvpModeEnabled, sqlDebug;
public String partyChatPrefix, noSkillBoundPlaceholder;
public ChatColor staminaFull, staminaHalf, staminaEmpty;
public long combatLogTimer, lootChestExpireTime, lootChestPlayerCooldown, globalSkillCooldown;
public double lootChestsChanceWeight, dropItemsChanceWeight, fishingDropsChanceWeight, partyMaxExpSplitRange;
public double lootChestsChanceWeight, dropItemsChanceWeight, fishingDropsChanceWeight, partyMaxExpSplitRange, pvpModeToggleOnCooldown, pvpModeToggleOffCooldown, pvpModeCombatCooldown, pvpModeCombatTimeout, pvpModeInvulnerability;
public int maxPartyLevelDifference, maxBoundActiveSkills, maxBoundPassiveSkills;
public final List<EntityDamageEvent.DamageCause> combatLogDamageCauses = new ArrayList<>();
@ -103,6 +104,7 @@ public class ConfigManager {
loadDefaultFile("conditions.yml");
loadDefaultFile("guilds.yml");
final ConfigurationSection config = MMOCore.plugin.getConfig();
commandVerbose.reload(MMOCore.plugin.getConfig().getConfigurationSection("command-verbose"));
messages = new ConfigFile("messages").getConfig();
@ -129,6 +131,15 @@ public class ConfigManager {
partyMaxExpSplitRange = MMOCore.plugin.getConfig().getDouble("party.max-exp-split-range");
splitProfessionExp = MMOCore.plugin.getConfig().getBoolean("party.profession-exp-split");
disableQuestBossBar = MMOCore.plugin.getConfig().getBoolean("mmocore-quests.disable-boss-bar");
sqlDebug = MMOCore.plugin.getConfig().getBoolean("mysql.debug");
// Combat
pvpModeEnabled = config.getBoolean("pvp_mode.enabled");
pvpModeToggleOnCooldown = config.getDouble("pvp_mode.cooldown.toggle_on");
pvpModeToggleOffCooldown = config.getDouble("pvp_mode.cooldown.toggle_off");
pvpModeCombatCooldown = config.getDouble("pvp_mode.cooldown.combat");
pvpModeCombatTimeout = config.getDouble("pvp_mode.combat_timeout");
pvpModeInvulnerability = config.getDouble("pvp_mode.invulnerability");
// Resources
staminaFull = getColorOrDefault("stamina-whole", ChatColor.GREEN);
@ -180,6 +191,10 @@ public class ConfigManager {
return messages.getStringList(key);
}
/**
* Merge with {@link net.Indyuce.mmocore.api.ConfigMessage}
*/
@Deprecated
public SimpleMessage getSimpleMessage(String key, String... placeholders) {
String format = messages.getString(key, "{MessageNotFound:\"" + key + "\"}");
for (int j = 0; j < placeholders.length - 1; j += 2)
@ -187,6 +202,10 @@ public class ConfigManager {
return new SimpleMessage(MythicLib.plugin.parseColors(format));
}
/**
* Merge with {@link net.Indyuce.mmocore.api.ConfigMessage}
*/
@Deprecated
public static class SimpleMessage {
private final String message;
private final boolean actionbar;

View File

@ -1,32 +0,0 @@
package net.Indyuce.mmocore.party;
import io.lumine.mythic.lib.comp.target.InteractionType;
import io.lumine.mythic.lib.comp.target.TargetRestriction;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.guild.AbstractGuild;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
public class MMOCoreTargetRestriction implements TargetRestriction {
@Override
public boolean canTarget(Player player, LivingEntity livingEntity, InteractionType interactionType) {
if (!interactionType.isOffense() || !(livingEntity instanceof Player))
return true;
PlayerData data = PlayerData.get(player);
// Check for party
AbstractParty party = MMOCore.plugin.partyModule.getParty(data);
if (party != null && party.hasMember((Player) livingEntity))
return false;
// Check for guild
AbstractGuild guild = MMOCore.plugin.guildModule.getGuild(data);
if (guild != null && guild.hasMember((Player) livingEntity))
return false;
return true;
}
}

View File

@ -0,0 +1,18 @@
package net.Indyuce.mmocore.party;
import io.lumine.mythic.lib.comp.interaction.relation.Relationship;
import io.lumine.mythic.lib.comp.interaction.relation.RelationshipHandler;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class PartyRelationHandler implements RelationshipHandler {
@NotNull
@Override
public Relationship getRelationship(Player player, Player target) {
final AbstractParty party = MMOCore.plugin.partyModule.getParty(PlayerData.get(player));
return party != null && party.hasMember(target) ? Relationship.PARTY_MEMBER : Relationship.PARTY_OTHER;
}
}

View File

@ -0,0 +1,111 @@
package net.Indyuce.mmocore.player;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.event.PlayerCombatEvent;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.util.Closable;
import net.Indyuce.mmocore.command.PvpModeCommand;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.Nullable;
public class CombatHandler implements Closable {
private final PlayerData player;
private final long firstHit = System.currentTimeMillis();
private long lastHit = System.currentTimeMillis();
private long lastInvulnerabilityApplication;
private boolean pvpMode;
@Nullable
private BukkitTask task;
public CombatHandler(PlayerData player) {
this.player = player;
}
public void update() {
lastHit = System.currentTimeMillis();
player.getMMOPlayerData().getCooldownMap().applyCooldown(PvpModeCommand.COOLDOWN_KEY, MMOCore.plugin.configManager.pvpModeCombatCooldown);
// Simply refreshing
if (isInCombat()) {
Bukkit.getScheduler().cancelTask(task.getTaskId());
task = Bukkit.getScheduler().runTaskLater(MMOCore.plugin, () -> quit(false), MMOCore.plugin.configManager.combatLogTimer / 50);
// Entering combat
} else {
MMOCore.plugin.configManager.getSimpleMessage("now-in-combat").send(player.getPlayer());
Bukkit.getPluginManager().callEvent(new PlayerCombatEvent(player, true));
}
}
public boolean isInPvpMode() {
return pvpMode;
}
public void setPvpMode(boolean pvpMode) {
this.pvpMode = pvpMode;
}
public long getLastHit() {
return lastHit;
}
public long getFirstHit() {
return firstHit;
}
/**
* Simply checks if there is a scheduled task.
*
* @return If the player is in combat
*/
public boolean isInCombat() {
return task != null;
}
/**
* This is used for PvP mode invulnerability when a player
* joins a region while he still has PvP mode toggled on.
*
* @return If the player is invulnerable
*/
public boolean isInvulnerable() {
return System.currentTimeMillis() < lastInvulnerabilityApplication + MMOCore.plugin.configManager.pvpModeInvulnerability * 1000;
}
public void applyInvulnerability() {
lastInvulnerabilityApplication = System.currentTimeMillis();
}
public boolean canQuitPvpMode() {
return System.currentTimeMillis() > lastHit + MMOCore.plugin.configManager.pvpModeCombatTimeout * 1000;
}
/**
* Quits combat. Throws an exception if player is not in combat.
* Can be called anytime when the player is in combat.
*
* @param cancelTask Should the running task be canceled.
*/
private void quit(boolean cancelTask) {
Validate.isTrue(isInCombat(), "Player not in combat");
if (cancelTask)
Bukkit.getScheduler().cancelTask(task.getTaskId());
task = null;
if (player.isOnline()) {
Bukkit.getPluginManager().callEvent(new PlayerCombatEvent(player, false));
MMOCore.plugin.configManager.getSimpleMessage("leave-combat").send(player.getPlayer());
}
}
@Override
public void close() {
if (isInCombat())
quit(true);
}
}

View File

@ -1,6 +1,7 @@
package net.Indyuce.mmocore.listener;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.event.PlayerAttackEvent;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.event.PlayerResourceUpdateEvent;
@ -9,7 +10,6 @@ import net.Indyuce.mmocore.api.player.profess.resource.PlayerResource;
import net.Indyuce.mmocore.gui.api.InventoryClickContext;
import net.Indyuce.mmocore.gui.api.PluginInventory;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
@ -58,7 +58,7 @@ public class PlayerListener implements Listener {
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void updateCombat(PlayerAttackEvent event) {
PlayerData.get(event.getAttacker().getPlayer()).updateCombat();
PlayerData.get(event.getAttacker().getPlayer()).getCombat().update();
}
/**
@ -66,8 +66,8 @@ public class PlayerListener implements Listener {
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void updateCombat(EntityDamageEvent event) {
if (event.getEntity() instanceof Player && MMOCore.plugin.configManager.combatLogDamageCauses.contains(event.getCause()))
PlayerData.get(event.getEntity().getUniqueId()).updateCombat();
if (UtilityMethods.isRealPlayer(event.getEntity()) && MMOCore.plugin.configManager.combatLogDamageCauses.contains(event.getCause()))
PlayerData.get(event.getEntity().getUniqueId()).getCombat().update();
}
@EventHandler

View File

@ -1,7 +1,7 @@
package net.Indyuce.mmocore.listener.option;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.comp.target.InteractionType;
import io.lumine.mythic.lib.comp.interaction.InteractionType;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.manager.InventoryManager;
import org.bukkit.entity.EntityType;

View File

@ -287,3 +287,28 @@ command-verbose:
reset: true
resource: true
waypoint: true
# Requires WorldGuard to work. Do NOT enable unless
# you have WG to avoid weird interact rule issues!
#
# More info available on the wiki
# https://gitlab.com/phoenix-dvpmt/mmocore/-/wikis/Combat#pvp-mode
pvp_mode:
# Requires /reload when changed
enabled: true
# Delay after any attack during which the player will stay in PvP Mode (seconds)
combat_timeout: 30
# Invulnerability when entering a pvp-mode region when your PvP mode is toggled on.
invulnerability: 60
cooldown:
# Delay before being able to use /pvpmode after being in combat (seconds)
# Has to be greater than the combat timeout for it to make sense
combat: 45
# Cooldown when toggling on PvP mode, before being able to toggle it off (seconds)
toggle_on: 5
# Cooldown when toggling off PvP mode (seconds)
toggle_off: 3

View File

@ -17,6 +17,9 @@ death-exp-loss:
- ''
# General
not-enough-perms: '&cYou do not have enough permissions.'
# Experience boosters
booster-main:
- '&e'
- '&eA &6{multiplier}x&e EXP multiplier is now active!'
@ -27,6 +30,14 @@ booster-skill:
- '&e'
booster-expired: '&cExpired!'
# PvP Mode
pvp-mode:
cooldown: '&cPlease wait {remaining} seconds to use this command again.'
toggle-on: '&aPvP Mode on.'
toggle-off: '&cPvP Mode off.'
leave: '&cYou left a PVP zone but are still vulnerable for {remaining} seconds!'
enter: '&aYou entered a PVP zone and gained invulnerability for {time} seconds!'
# Fishing Profession
caught-fish: '&cYou caught a fish!'
fish-out-water: '&aWell done!'
@ -150,10 +161,6 @@ cant-redo-quest: '&cYou can''t start this quest twice.'
quest-cooldown: '&cYou need to wait {delay}.'
start-quest: '&eYou successfully started &6{quest}&e.'
cant-choose-new-class:
- '&cYou need one class point to perform this action.'
no-permission-for-class:
- "&cYou don't have the permission to choose this class."
# Attributes
no-attribute-points-spent: '&cYou have not spent any attribute points.'
not-attribute-reallocation-point: '&cYou do not have 1 reallocation point.'
@ -163,6 +170,12 @@ attribute-points-reallocated: '&eYou successfully reset your attributes. You now
attribute-max-points-hit: '&cYou cannot level up this attribute anymore.'
attribute-level-up: '&eYou successfully leveled up your &6{attribute}&e.' # {level}
# Class selection
cant-choose-new-class:
- '&cYou need one class point to perform this action.'
no-permission-for-class:
- "&cYou don't have the permission to choose this class."
# Skills
no-class-skill: '&cYour class has no skill.'
not-enough-skill-points: '&cYou need one skill point.'
@ -178,6 +191,7 @@ not-skill-reallocation-point: '&cYou do not have 1 skill reallocation point.'
no-skill-points-spent: '&cYou have not spent any skill points.'
skill-points-reallocated: '&eYou successfully reset your attributes. You now have &6{points} &eskill points.'
max-points-reached: '&cYou reached the maximum points you can spend. You need to reallocate your points to rollback.'
# Skill Trees
no-skill-tree-points-spent: '&cYou have not spent any skill tree points.'
locked-node: '&cThis skill is locked!'

View File

@ -18,6 +18,9 @@ permissions:
mmocore.waypoints:
description: Access to /waypoints
default: op
mmocore.pvpmode:
description: Access to /pvpmode
default: op
mmocore.currency:
description: Access to /deposit and /withdraw
default: op