diff --git a/pom.xml b/pom.xml index 00f291ec..6ce28081 100644 --- a/pom.xml +++ b/pom.xml @@ -170,13 +170,6 @@ provided - - com.bekvon.bukkit.residence - Residence - 4.8.7.2 - provided - - net.citizensnpcs Citizens diff --git a/src/main/java/net/Indyuce/mmocore/MMOCore.java b/src/main/java/net/Indyuce/mmocore/MMOCore.java index 7541e3ca..58ccdf20 100644 --- a/src/main/java/net/Indyuce/mmocore/MMOCore.java +++ b/src/main/java/net/Indyuce/mmocore/MMOCore.java @@ -1,10 +1,12 @@ package net.Indyuce.mmocore; +import io.lumine.mythic.lib.MythicLib; import io.lumine.mythic.lib.comp.Metrics; import io.lumine.mythic.lib.version.SpigotPlugin; import io.lumine.mythic.utils.plugin.LuminePlugin; import net.Indyuce.mmocore.api.ConfigFile; import net.Indyuce.mmocore.api.PlayerActionBar; +import net.Indyuce.mmocore.comp.MMOCoreTargetRestriction; import net.Indyuce.mmocore.loot.chest.LootChest; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.profess.resource.PlayerResource; @@ -16,10 +18,6 @@ import net.Indyuce.mmocore.comp.anticheat.AntiCheatSupport; import net.Indyuce.mmocore.comp.anticheat.SpartanPlugin; import net.Indyuce.mmocore.comp.citizens.CitizenInteractEventListener; import net.Indyuce.mmocore.comp.citizens.CitizensMMOLoader; -import net.Indyuce.mmocore.comp.flags.DefaultFlags; -import net.Indyuce.mmocore.comp.flags.FlagPlugin; -import net.Indyuce.mmocore.comp.flags.ResidenceFlags; -import net.Indyuce.mmocore.comp.flags.WorldGuardFlags; import net.Indyuce.mmocore.comp.mythicmobs.MythicHook; import net.Indyuce.mmocore.comp.mythicmobs.MythicMobsMMOLoader; import net.Indyuce.mmocore.comp.placeholder.DefaultParser; @@ -67,7 +65,6 @@ public class MMOCore extends LuminePlugin { public VaultEconomy economy; public AntiCheatSupport antiCheatSupport; public RegionHandler regionHandler = new DefaultRegionHandler(); - public FlagPlugin flagPlugin = new DefaultFlags(); public PlaceholderParser placeholderParser = new DefaultParser(); public DataProvider dataProvider = new YAMLDataProvider(); public final PlayerActionBar actionBarManager = new PlayerActionBar(); @@ -101,6 +98,9 @@ public class MMOCore extends LuminePlugin { public void load() { + // Register target restrictions due to MMOCore in MythicLib + MythicLib.plugin.getEntities().registerRestriction(new MMOCoreTargetRestriction()); + /* * register extra objective, drop items... */ @@ -119,7 +119,7 @@ public class MMOCore extends LuminePlugin { * WorldGuard closes the flag registry after 'onLoad()', so it must be * registered here or it will throw an IllegalStateException */ - if (Bukkit.getPluginManager().getPlugin("WorldGuard") != null) flagPlugin = new WorldGuardFlags(); + // if (Bukkit.getPluginManager().getPlugin("WorldGuard") != null) flagPlugin = new WorldGuardFlags(); } public void enable() { @@ -156,9 +156,6 @@ public class MMOCore extends LuminePlugin { if (Bukkit.getPluginManager().getPlugin("WorldGuard") != null) { regionHandler = new WorldGuardRegionHandler(); getLogger().log(Level.INFO, "Hooked onto WorldGuard"); - } else if (Bukkit.getPluginManager().getPlugin("Residence") != null) { - flagPlugin = new ResidenceFlags(); - getLogger().log(Level.INFO, "Hooked onto Residence"); } if (Bukkit.getPluginManager().getPlugin("Spartan") != null) { diff --git a/src/main/java/net/Indyuce/mmocore/api/player/social/Party.java b/src/main/java/net/Indyuce/mmocore/api/player/social/Party.java index 046fe4da..244167e2 100644 --- a/src/main/java/net/Indyuce/mmocore/api/player/social/Party.java +++ b/src/main/java/net/Indyuce/mmocore/api/player/social/Party.java @@ -1,159 +1,162 @@ package net.Indyuce.mmocore.api.player.social; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.function.Consumer; - -import org.bukkit.entity.Player; - import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.ConfigMessage; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.gui.api.PluginInventory; import net.Indyuce.mmocore.gui.social.party.EditablePartyView.PartyViewInventory; import net.Indyuce.mmocore.manager.InventoryManager; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.function.Consumer; public class Party { - private final PartyMembers members = new PartyMembers(); - private final Map invites = new HashMap<>(); + private final PartyMembers members = new PartyMembers(); + private final Map invites = new HashMap<>(); - // used to check if two parties are the same - private final UUID id = UUID.randomUUID(); + // used to check if two parties are the same + private final UUID id = UUID.randomUUID(); - /* - * owner changes when the old owner leaves party - */ - private PlayerData owner; + /* + * owner changes when the old owner leaves party + */ + private PlayerData owner; - public Party(PlayerData owner) { - this.owner = owner; - addMember(owner); - } + public Party(PlayerData owner) { + this.owner = owner; + addMember(owner); + } - public UUID getUniqueId() { - return id; - } - - public PlayerData getOwner() { - return owner; - } + public UUID getUniqueId() { + return id; + } - public PartyMembers getMembers() { - return members; - } + public PlayerData getOwner() { + return owner; + } - public long getLastInvite(Player player) { - return invites.containsKey(player.getUniqueId()) ? invites.get(player.getUniqueId()) : 0; - } + public PartyMembers getMembers() { + return members; + } - public void removeLastInvite(Player player) { - invites.remove(player.getUniqueId()); - } + public long getLastInvite(Player player) { + return invites.containsKey(player.getUniqueId()) ? invites.get(player.getUniqueId()) : 0; + } - public void removeMember(PlayerData data) { - removeMember(data, true); - } - - public void removeMember(PlayerData data, boolean notify) { - if (data.isOnline() && data.getPlayer().getOpenInventory() != null - && data.getPlayer().getOpenInventory().getTopInventory().getHolder() instanceof PartyViewInventory) - InventoryManager.PARTY_CREATION.newInventory(data).open(); + public void removeLastInvite(Player player) { + invites.remove(player.getUniqueId()); + } - members.remove(data); - data.setParty(null); + public void removeMember(PlayerData data) { + removeMember(data, true); + } - reopenInventories(); + public void removeMember(PlayerData data, boolean notify) { + if (data.isOnline() && data.getPlayer().getOpenInventory() != null + && data.getPlayer().getOpenInventory().getTopInventory().getHolder() instanceof PartyViewInventory) + InventoryManager.PARTY_CREATION.newInventory(data).open(); - // disband the party if no member left - if (members.count() < 1) { - MMOCore.plugin.partyManager.unregisterParty(this); - return; - } + members.remove(data); + data.setParty(null); - // transfer ownership - if (owner.equals(data)) { - owner = members.get(0); - if(notify && owner.isOnline()) MMOCore.plugin.configManager.getSimpleMessage("transfer-party-ownership").send(owner.getPlayer()); - } - } + reopenInventories(); - public void addMember(PlayerData data) { - if (data.hasParty()) - data.getParty().removeMember(data); + // disband the party if no member left + if (members.count() < 1) { + MMOCore.plugin.partyManager.unregisterParty(this); + return; + } - data.setParty(this); - members.add(data); + // transfer ownership + if (owner.equals(data)) { + owner = members.get(0); + if (notify && owner.isOnline()) + MMOCore.plugin.configManager.getSimpleMessage("transfer-party-ownership").send(owner.getPlayer()); + } + } - reopenInventories(); - } + public void addMember(PlayerData data) { + if (data.hasParty()) + data.getParty().removeMember(data); - public void reopenInventories() { - for (PlayerData member : members.members) - if (member.isOnline() && member.getPlayer().getOpenInventory() != null - && member.getPlayer().getOpenInventory().getTopInventory().getHolder() instanceof PartyViewInventory) - ((PluginInventory) member.getPlayer().getOpenInventory().getTopInventory().getHolder()).open(); - } + data.setParty(this); + members.add(data); - public void sendPartyInvite(PlayerData inviter, PlayerData target) { - invites.put(target.getUniqueId(), System.currentTimeMillis()); - Request request = new PartyInvite(this, inviter, target); - if(inviter.isOnline() && target.isOnline()) - new ConfigMessage("party-invite").addPlaceholders("player", inviter.getPlayer().getName(), "uuid", request.getUniqueId().toString()) - .sendAsJSon(target.getPlayer()); - MMOCore.plugin.requestManager.registerRequest(request); - } + reopenInventories(); + } - @Override - public boolean equals(Object obj) { - return obj instanceof Party && ((Party) obj).getUniqueId().equals(getUniqueId()); - } + public void reopenInventories() { + for (PlayerData member : members.members) + if (member.isOnline() && member.getPlayer().getOpenInventory() != null + && member.getPlayer().getOpenInventory().getTopInventory().getHolder() instanceof PartyViewInventory) + ((PluginInventory) member.getPlayer().getOpenInventory().getTopInventory().getHolder()).open(); + } - /* - * this class makes controling entries and departures and APPLYING PARTY - * STAT ATTRIBUTES much easier - */ - public static class PartyMembers { - private final List members = new ArrayList<>(); + public void sendPartyInvite(PlayerData inviter, PlayerData target) { + invites.put(target.getUniqueId(), System.currentTimeMillis()); + Request request = new PartyInvite(this, inviter, target); + if (inviter.isOnline() && target.isOnline()) + new ConfigMessage("party-invite").addPlaceholders("player", inviter.getPlayer().getName(), "uuid", request.getUniqueId().toString()) + .sendAsJSon(target.getPlayer()); + MMOCore.plugin.requestManager.registerRequest(request); + } - public PlayerData get(int count) { - return members.get(count); - } + @Override + public boolean equals(Object obj) { + return obj instanceof Party && ((Party) obj).getUniqueId().equals(getUniqueId()); + } - public boolean has(PlayerData player) { - return members.contains(player); - } + /* + * this class makes controling entries and departures and APPLYING PARTY + * STAT ATTRIBUTES much easier + */ + public static class PartyMembers { + private final List members = new ArrayList<>(); - public void add(PlayerData player) { - members.add(player); + public PlayerData get(int count) { + return members.get(count); + } - members.forEach(this::applyAttributes); - } + public boolean has(PlayerData player) { + return members.contains(player); + } - public void remove(PlayerData player) { - members.remove(player); + public boolean has(Player player) { + for (PlayerData member : members) + if (member.getUniqueId().equals(player.getUniqueId())) + return true; + return false; + } - members.forEach(this::applyAttributes); - clearAttributes(player); - } + public void add(PlayerData player) { + members.add(player); - public void forEach(Consumer action) { - members.forEach(action); - } + members.forEach(this::applyAttributes); + } - public int count() { - return members.size(); - } + public void remove(PlayerData player) { + members.remove(player); - private void applyAttributes(PlayerData player) { - MMOCore.plugin.partyManager.getBonuses().forEach(stat -> player.getStats().getInstance(stat).addModifier("mmocoreParty", - MMOCore.plugin.partyManager.getBonus(stat).multiply(members.size() - 1))); - } + members.forEach(this::applyAttributes); + clearAttributes(player); + } - private void clearAttributes(PlayerData player) { - MMOCore.plugin.partyManager.getBonuses().forEach(stat -> player.getStats().getInstance(stat).remove("mmocoreParty")); - } - } + public void forEach(Consumer action) { + members.forEach(action); + } + + public int count() { + return members.size(); + } + + private void applyAttributes(PlayerData player) { + MMOCore.plugin.partyManager.getBonuses().forEach(stat -> player.getStats().getInstance(stat).addModifier("mmocoreParty", + MMOCore.plugin.partyManager.getBonus(stat).multiply(members.size() - 1))); + } + + private void clearAttributes(PlayerData player) { + MMOCore.plugin.partyManager.getBonuses().forEach(stat -> player.getStats().getInstance(stat).remove("mmocoreParty")); + } + } } diff --git a/src/main/java/net/Indyuce/mmocore/api/util/MMOCoreUtils.java b/src/main/java/net/Indyuce/mmocore/api/util/MMOCoreUtils.java index e4a19923..4e695144 100644 --- a/src/main/java/net/Indyuce/mmocore/api/util/MMOCoreUtils.java +++ b/src/main/java/net/Indyuce/mmocore/api/util/MMOCoreUtils.java @@ -2,6 +2,7 @@ package net.Indyuce.mmocore.api.util; import io.lumine.mythic.lib.MythicLib; import io.lumine.mythic.lib.api.item.NBTItem; +import io.lumine.mythic.lib.comp.target.InteractionType; import io.lumine.mythic.lib.version.VersionMaterial; import io.lumine.mythic.utils.holograms.Hologram; import io.lumine.mythic.utils.serialize.Position; @@ -33,216 +34,207 @@ import java.util.Arrays; import java.util.List; public class MMOCoreUtils { - public static boolean pluginItem(ItemStack item) { - return item != null && item.hasItemMeta() && item.getItemMeta().hasDisplayName(); - } + public static boolean pluginItem(ItemStack item) { + return item != null && item.hasItemMeta() && item.getItemMeta().hasDisplayName(); + } - public static String displayName(ItemStack item) { - return item.hasItemMeta() && item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName() - : caseOnWords(item.getType().name().replace("_", " ")); - } + public static String displayName(ItemStack item) { + return item.hasItemMeta() && item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName() + : caseOnWords(item.getType().name().replace("_", " ")); + } - public static String caseOnWords(String s) { - StringBuilder builder = new StringBuilder(s); - boolean isLastSpace = true; - for (int item = 0; item < builder.length(); item++) { - char ch = builder.charAt(item); - if (isLastSpace && ch >= 'a' && ch <= 'z') { - builder.setCharAt(item, (char) (ch + ('A' - 'a'))); - isLastSpace = false; - } else - isLastSpace = ch == ' '; - } - return builder.toString(); - } + public static String caseOnWords(String s) { + StringBuilder builder = new StringBuilder(s); + boolean isLastSpace = true; + for (int item = 0; item < builder.length(); item++) { + char ch = builder.charAt(item); + if (isLastSpace && ch >= 'a' && ch <= 'z') { + builder.setCharAt(item, (char) (ch + ('A' - 'a'))); + isLastSpace = false; + } else + isLastSpace = ch == ' '; + } + return builder.toString(); + } - /** - * Displays an in game indicator using a hologram. This uses - * LumineUtils hologramFactory to summon holograms - *

- * The hologram despawns after 1sec - * - * @param loc Target location - * @param message Message to display - */ - public static void displayIndicator(Location loc, String message) { - Hologram holo = Hologram.create(Position.of(loc), Arrays.asList(message)); - Bukkit.getScheduler().runTaskLater(MMOCore.plugin, () -> holo.despawn(), 20); - } + /** + * Displays an in game indicator using a hologram. This uses + * LumineUtils hologramFactory to summon holograms + *

+ * The hologram despawns after 1sec + * + * @param loc Target location + * @param message Message to display + */ + public static void displayIndicator(Location loc, String message) { + Hologram holo = Hologram.create(Position.of(loc), Arrays.asList(message)); + Bukkit.getScheduler().runTaskLater(MMOCore.plugin, () -> holo.despawn(), 20); + } - public static boolean isPlayerHead(Material material) { - return material == VersionMaterial.PLAYER_HEAD.toMaterial() || material == VersionMaterial.PLAYER_WALL_HEAD.toMaterial(); - } + public static boolean isPlayerHead(Material material) { + return material == VersionMaterial.PLAYER_HEAD.toMaterial() || material == VersionMaterial.PLAYER_WALL_HEAD.toMaterial(); + } - public static ItemStack readIcon(String string) throws IllegalArgumentException { - String[] split = string.split(":"); - Material material = Material.valueOf(split[0].toUpperCase().replace("-", "_").replace(" ", "_")); - return split.length > 1 ? MythicLib.plugin.getVersion().getWrapper().textureItem(material, Integer.parseInt(split[1])) : new ItemStack(material); - } + public static ItemStack readIcon(String string) throws IllegalArgumentException { + String[] split = string.split(":"); + Material material = Material.valueOf(split[0].toUpperCase().replace("-", "_").replace(" ", "_")); + return split.length > 1 ? MythicLib.plugin.getVersion().getWrapper().textureItem(material, Integer.parseInt(split[1])) : new ItemStack(material); + } - public static int getWorth(ItemStack[] items) { - int t = 0; - for (ItemStack item : items) - if (item != null && item.getType() != Material.AIR) - t += MythicLib.plugin.getVersion().getWrapper().getNBTItem(item).getInteger("RpgWorth") * item.getAmount(); - return t; - } + public static int getWorth(ItemStack[] items) { + int t = 0; + for (ItemStack item : items) + if (item != null && item.getType() != Material.AIR) + t += MythicLib.plugin.getVersion().getWrapper().getNBTItem(item).getInteger("RpgWorth") * item.getAmount(); + return t; + } - public static String toBase64(ItemStack[] items) { - try { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream); - dataOutput.writeInt(items.length); - for (ItemStack item : items) - dataOutput.writeObject(item); - dataOutput.close(); - return Base64Coder.encodeLines(outputStream.toByteArray()); - } catch (Exception e) { - return null; - } - } + public static String toBase64(ItemStack[] items) { + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream); + dataOutput.writeInt(items.length); + for (ItemStack item : items) + dataOutput.writeObject(item); + dataOutput.close(); + return Base64Coder.encodeLines(outputStream.toByteArray()); + } catch (Exception e) { + return null; + } + } - public static ItemStack[] itemStackArrayFromBase64(String data) { - try { - ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data)); - BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream); - ItemStack[] items = new ItemStack[dataInput.readInt()]; - for (int i = 0; i < items.length; i++) - items[i] = (ItemStack) dataInput.readObject(); - dataInput.close(); - return items; - } catch (Exception e) { - return null; - } - } + public static ItemStack[] itemStackArrayFromBase64(String data) { + try { + ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data)); + BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream); + ItemStack[] items = new ItemStack[dataInput.readInt()]; + for (int i = 0; i < items.length; i++) + items[i] = (ItemStack) dataInput.readObject(); + dataInput.close(); + return items; + } catch (Exception e) { + return null; + } + } - private static final String[] romanChars = { "I", "IV", "V", "IX", "X", "XL", "L", "XC", "C", "CD", "D", "CM", "M" }; - private static final int[] romanQuantities = { 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000 }; + private static final String[] romanChars = {"I", "IV", "V", "IX", "X", "XL", "L", "XC", "C", "CD", "D", "CM", "M"}; + private static final int[] romanQuantities = {1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000}; - public static String intToRoman(int input) { - if (input < 1) - return "<1"; - if (input > 3999) - return ">3999"; + public static String intToRoman(int input) { + if (input < 1) + return "<1"; + if (input > 3999) + return ">3999"; - StringBuilder result = new StringBuilder(); + StringBuilder result = new StringBuilder(); - for (int j = 0; j < romanChars.length; j++) { - int i = romanChars.length - j - 1; - int q = romanQuantities[i]; - String c = romanChars[i]; + for (int j = 0; j < romanChars.length; j++) { + int i = romanChars.length - j - 1; + int q = romanQuantities[i]; + String c = romanChars[i]; - while (input >= q) { - result.append(c); - input -= q; - } - } + while (input >= q) { + result.append(c); + input -= q; + } + } - return result.toString(); - } + return result.toString(); + } - /* - * method to get all entities surrounding a location. this method does not - * take every entity in the world but rather takes all the entities from the - * 9 chunks around the entity, so even if the location is at the border of a - * chunk (worst case border of 4 chunks), the entity will still be included - */ - public static List getNearbyChunkEntities(Location loc) { - /* - * another method to save performance is if an entity bounding box - * calculation is made twice in the same tick then the method does not - * need to be called twice, it can utilize the same entity list since - * the entities have not moved (e.g fireball which does 2+ calculations - * per tick) - */ - List entities = new ArrayList<>(); + /** + * Method to get all entities surrounding a location. This method does not + * take every entity in the world but rather takes all the entities from the + * 9 chunks around the entity, so even if the location is at the border of a + * chunk (worst case border of 4 chunks), the entity will still be included + */ + public static List getNearbyChunkEntities(Location loc) { - int cx = loc.getChunk().getX(); - int cz = loc.getChunk().getZ(); + /* + * Another method to save performance is: if an entity bounding box + * calculation is made twice in the same tick, then the method does not + * need to be called twice, it can utilize the same entity list since + * the entities have not moved (e.g fireball which does 2+ calculations + * per tick) + * + * Of course we're assuming that the projectile does move at a speed + * lower than 1 chunk per second which is most likely true, otherwise + * just use ray casting. + */ + List entities = new ArrayList<>(); - for (int x = -1; x < 2; x++) - for (int z = -1; z < 2; z++) - entities.addAll(Arrays.asList(loc.getWorld().getChunkAt(cx + x, cz + z).getEntities())); + int cx = loc.getChunk().getX(); + int cz = loc.getChunk().getZ(); - return entities; - } + for (int x = -1; x < 2; x++) + for (int z = -1; z < 2; z++) + entities.addAll(Arrays.asList(loc.getWorld().getChunkAt(cx + x, cz + z).getEntities())); - /** - * @param player Player casting a spell/basic attack - * @param target The target entity - * @return If the player can attack the entity - */ - public static boolean canTarget(PlayerData player, Entity target) { - return canTarget(player, target, false); - } + return entities; + } - /** - * @param player Player casting a spell/basic attack - * @param target The target entity - * @param buff Used when the attack is not an attack but a positive buff. - * Buffs or heals can be applied on party members, whereas - * attacks can't target party members. - * @return If the player can target the entity given the attack type - * (buff or attack) - */ - // TODO worldguard flags support for no target - public static boolean canTarget(PlayerData player, Entity target, boolean buff) { + /** + * @param player Player casting a spell/basic attack + * @param target The target entity + * @return If the player can attack the entity + */ + public static boolean canTarget(PlayerData player, Entity target) { + return canTarget(player, target, InteractionType.OFFENSE_SKILL); + } - // Basic checks - if (!player.isOnline() || !(target instanceof LivingEntity) || player.getPlayer().equals(target) || target.isDead() || MythicLib.plugin.getEntities().findCustom(target)) - return false; + /** + * @param player Player casting a spell/basic attack + * @param target The target entity + * @param interaction Whether the action is an attack or a buff, this does + * impact the outcome of the friendly fire check + * @return If the player can target the entity given the attack type (buff or attack) + */ + public static boolean canTarget(PlayerData player, Entity target, InteractionType interaction) { + return target instanceof LivingEntity && MythicLib.plugin.getEntities().canTarget(player.getPlayer(), (LivingEntity) target, interaction); + } - // Party check - if (!buff && target instanceof Player) { - PlayerData targetData = PlayerData.get((Player) target); - return !targetData.hasParty() || !targetData.getParty().getMembers().has(player); - } + public static void heal(LivingEntity target, double value) { + double max = target.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue(); + double gain = Math.min(max, target.getHealth() + value) - target.getHealth(); - return true; - } + EntityRegainHealthEvent event = new EntityRegainHealthEvent(target, gain, RegainReason.CUSTOM); + Bukkit.getPluginManager().callEvent(event); + if (!event.isCancelled()) + target.setHealth(target.getHealth() + gain); + } - public static void heal(LivingEntity target, double value) { - double max = target.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue(); - double gain = Math.min(max, target.getHealth() + value) - target.getHealth(); + /** + * Method used when mining a custom block or fishing, as the corresponding + * interaction event is cancelled durability is not handled. This method is + * needed and actually calls a damage event so that MMOItems can listen to + * it + * + * @param player Player holding the item with durability + * @param slot The slot of the item with durability + * @param damage Damage that needs to be applied + */ + public static void decreaseDurability(Player player, EquipmentSlot slot, int damage) { + ItemStack item = player.getInventory().getItem(slot); + NBTItem nbt = NBTItem.get(item); - EntityRegainHealthEvent event = new EntityRegainHealthEvent(target, gain, RegainReason.CUSTOM); - Bukkit.getPluginManager().callEvent(event); - if (!event.isCancelled()) - target.setHealth(target.getHealth() + gain); - } + if (!nbt.hasTag("MMOITEMS_MAX_DURABILITY")) { + return; + } - /** - * Method used when mining a custom block or fishing, as the corresponding - * interaction event is cancelled durability is not handled. This method is - * needed and actually calls a damage event so that MMOItems can listen to - * it - * - * @param player Player holding the item with durability - * @param slot The slot of the item with durability - * @param damage Damage that needs to be applied - */ - public static void decreaseDurability(Player player, EquipmentSlot slot, int damage) { - ItemStack item = player.getInventory().getItem(slot); - NBTItem nbt = NBTItem.get(item); - - if(!nbt.hasTag("MMOITEMS_MAX_DURABILITY")){ - return; - } - - PlayerItemDamageEvent event = new PlayerItemDamageEvent(player, item, damage); - Bukkit.getPluginManager().callEvent(event); - if (event.isCancelled()) - return; + PlayerItemDamageEvent event = new PlayerItemDamageEvent(player, item, damage); + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) + return; - if (!nbt.getBoolean("Unbreakable") && item.getItemMeta() instanceof Damageable) { - ItemMeta meta = item.getItemMeta(); - ((Damageable) meta).setDamage(((Damageable) meta).getDamage() + damage); - item.setItemMeta(meta); - if (((Damageable) meta).getDamage() >= item.getType().getMaxDurability()) { - player.playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, 1F, 1F); - player.getInventory().setItem(slot, null); - } - } - } + if (!nbt.getBoolean("Unbreakable") && item.getItemMeta() instanceof Damageable) { + ItemMeta meta = item.getItemMeta(); + ((Damageable) meta).setDamage(((Damageable) meta).getDamage() + damage); + item.setItemMeta(meta); + if (((Damageable) meta).getDamage() >= item.getType().getMaxDurability()) { + player.playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, 1F, 1F); + player.getInventory().setItem(slot, null); + } + } + } } diff --git a/src/main/java/net/Indyuce/mmocore/comp/MMOCoreTargetRestriction.java b/src/main/java/net/Indyuce/mmocore/comp/MMOCoreTargetRestriction.java new file mode 100644 index 00000000..44dc5470 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/comp/MMOCoreTargetRestriction.java @@ -0,0 +1,24 @@ +package net.Indyuce.mmocore.comp; + +import io.lumine.mythic.lib.comp.target.InteractionType; +import io.lumine.mythic.lib.comp.target.TargetRestriction; +import net.Indyuce.mmocore.api.player.PlayerData; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; + +public class MMOCoreTargetRestriction implements TargetRestriction { + + @Override + public boolean canTarget(Player player, LivingEntity target, InteractionType interaction) { + + if (interaction.isOffense() && target instanceof Player) { + PlayerData targetData = PlayerData.get(target.getUniqueId()); + + // Check for the same party + if (targetData.hasParty() && targetData.getParty().getMembers().has(player)) + return false; + } + + return true; + } +} diff --git a/src/main/java/net/Indyuce/mmocore/comp/flags/DefaultFlags.java b/src/main/java/net/Indyuce/mmocore/comp/flags/DefaultFlags.java deleted file mode 100644 index 1d45ad7d..00000000 --- a/src/main/java/net/Indyuce/mmocore/comp/flags/DefaultFlags.java +++ /dev/null @@ -1,21 +0,0 @@ -package net.Indyuce.mmocore.comp.flags; - -import org.bukkit.Location; -import org.bukkit.entity.Player; - -public class DefaultFlags implements FlagPlugin { -// @Override -// public boolean isPvpAllowed(Location loc) { -// return true; -// } - - @Override - public boolean isFlagAllowed(Player player, CustomFlag flag) { - return true; - } - - @Override - public boolean isFlagAllowed(Location loc, CustomFlag flag) { - return true; - } -} diff --git a/src/main/java/net/Indyuce/mmocore/comp/flags/FlagPlugin.java b/src/main/java/net/Indyuce/mmocore/comp/flags/FlagPlugin.java deleted file mode 100644 index 4c986c5f..00000000 --- a/src/main/java/net/Indyuce/mmocore/comp/flags/FlagPlugin.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.Indyuce.mmocore.comp.flags; - -import org.bukkit.Location; -import org.bukkit.entity.Player; - -public interface FlagPlugin { -// public boolean isPvpAllowed(Location loc); - - boolean isFlagAllowed(Player player, CustomFlag flag); - - boolean isFlagAllowed(Location loc, CustomFlag flag); - - enum CustomFlag { - SKILLS; - - public String getPath() { - return "mmocore-" + name().toLowerCase(); - } - } -} diff --git a/src/main/java/net/Indyuce/mmocore/comp/flags/ResidenceFlags.java b/src/main/java/net/Indyuce/mmocore/comp/flags/ResidenceFlags.java deleted file mode 100644 index dc605c0d..00000000 --- a/src/main/java/net/Indyuce/mmocore/comp/flags/ResidenceFlags.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.Indyuce.mmocore.comp.flags; - -import org.bukkit.Location; -import org.bukkit.entity.Player; - -import com.bekvon.bukkit.residence.Residence; -import com.bekvon.bukkit.residence.protection.ClaimedResidence; -import com.bekvon.bukkit.residence.protection.FlagPermissions; - -public class ResidenceFlags implements FlagPlugin { - public ResidenceFlags() { - for (CustomFlag flag : CustomFlag.values()) - FlagPermissions.addFlag(flag.getPath()); - } - -// @Override -// public boolean isPvpAllowed(Location loc) { -// ClaimedResidence res = Residence.getInstance().getResidenceManager().getByLoc(loc); -// return res == null || res.getPermissions().has(Flags.pvp, true); -// } - - @SuppressWarnings("deprecation") - @Override - public boolean isFlagAllowed(Player player, CustomFlag flag) { - ClaimedResidence res = Residence.getInstance().getResidenceManager().getByLoc(player); - return res == null || res.getPermissions().playerHas(player, flag.getPath(), true); - } - - @Override - public boolean isFlagAllowed(Location loc, CustomFlag flag) { - ClaimedResidence res = Residence.getInstance().getResidenceManager().getByLoc(loc); - return res == null || res.getPermissions().has(flag.getPath(), true, true); - } -} \ No newline at end of file diff --git a/src/main/java/net/Indyuce/mmocore/comp/flags/WorldGuardFlags.java b/src/main/java/net/Indyuce/mmocore/comp/flags/WorldGuardFlags.java deleted file mode 100644 index 982416f3..00000000 --- a/src/main/java/net/Indyuce/mmocore/comp/flags/WorldGuardFlags.java +++ /dev/null @@ -1,66 +0,0 @@ -package net.Indyuce.mmocore.comp.flags; - -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Level; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.entity.Player; - -import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldguard.WorldGuard; -import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.flags.StateFlag; -import com.sk89q.worldguard.protection.flags.registry.FlagRegistry; - -import net.Indyuce.mmocore.MMOCore; - -public class WorldGuardFlags implements FlagPlugin { - private final WorldGuard worldguard; - private final WorldGuardPlugin worldguardPlugin; - private final Map flags = new HashMap<>(); - - public WorldGuardFlags() { - this.worldguard = WorldGuard.getInstance(); - this.worldguardPlugin = ((WorldGuardPlugin) Bukkit.getServer().getPluginManager() - .getPlugin("WorldGuard")); - - FlagRegistry registry = worldguard.getFlagRegistry(); - for (CustomFlag customFlag : CustomFlag.values()) { - StateFlag flag = new StateFlag(customFlag.getPath(), true); - try { - registry.register(flag); - flags.put(customFlag.getPath(), flag); - MMOCore.debug(1, "[FLAGDEBUG] Registered WG Flag\n" - + " - Info{name=" + flag.getName() + ",path=" + customFlag.getPath() + "}"); - } catch (Exception exception) { - MMOCore.debug(1, Level.SEVERE, "[FLAGDEBUG] FAILED to register WG Flag\n" - + " - Info{name=" + flag.getName() + ",path=" + customFlag.getPath() + "}"); - exception.printStackTrace(); - } - } - } - -// @Override -// public boolean isPvpAllowed(Location loc) { -// return getApplicableRegion(loc).queryState(null, Flags.PVP) != StateFlag.State.DENY; -// } - - @Override - public boolean isFlagAllowed(Location loc, CustomFlag customFlag) { - return getApplicableRegion(loc).queryValue(null, flags.get(customFlag.getPath())) != StateFlag.State.DENY; - } - - @Override - public boolean isFlagAllowed(Player player, CustomFlag customFlag) { - return getApplicableRegion(player.getLocation()).queryValue(worldguardPlugin.wrapPlayer(player), - flags.get(customFlag.getPath())) != StateFlag.State.DENY; - } - - private ApplicableRegionSet getApplicableRegion(Location loc) { - return worldguard.getPlatform().getRegionContainer().createQuery() - .getApplicableRegions(BukkitAdapter.adapt(loc)); - } -} diff --git a/src/main/java/net/Indyuce/mmocore/comp/mythicmobs/skill/handlers/PlayerDamageByEntitySkillHandler.java b/src/main/java/net/Indyuce/mmocore/comp/mythicmobs/skill/handlers/PlayerDamageByEntitySkillHandler.java index 8a400716..f7d386eb 100644 --- a/src/main/java/net/Indyuce/mmocore/comp/mythicmobs/skill/handlers/PlayerDamageByEntitySkillHandler.java +++ b/src/main/java/net/Indyuce/mmocore/comp/mythicmobs/skill/handlers/PlayerDamageByEntitySkillHandler.java @@ -1,5 +1,6 @@ package net.Indyuce.mmocore.comp.mythicmobs.skill.handlers; +import io.lumine.mythic.lib.comp.target.InteractionType; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.util.MMOCoreUtils; import net.Indyuce.mmocore.comp.mythicmobs.skill.MythicSkill; @@ -20,7 +21,7 @@ public class PlayerDamageByEntitySkillHandler extends PassiveMythicSkillHandler @EventHandler private void a(EntityDamageByEntityEvent event) { - if (event.getEntity().getType() == EntityType.PLAYER && MMOCoreUtils.canTarget(PlayerData.get(event.getEntity().getUniqueId()), event.getDamager())) + if (event.getEntity().getType() == EntityType.PLAYER && MMOCoreUtils.canTarget(PlayerData.get(event.getEntity().getUniqueId()), event.getDamager(), InteractionType.OFFENSE_SKILL)) castSkill(PlayerData.get((Player) event.getEntity()), event.getDamager()); } } diff --git a/src/main/java/net/Indyuce/mmocore/listener/PartyListener.java b/src/main/java/net/Indyuce/mmocore/listener/PartyListener.java index 2c80f5c1..72e6da93 100644 --- a/src/main/java/net/Indyuce/mmocore/listener/PartyListener.java +++ b/src/main/java/net/Indyuce/mmocore/listener/PartyListener.java @@ -43,9 +43,12 @@ public class PartyListener implements Listener { }); } - /* - * cancel damage of players from the same party + /** + * Cancel damage of players from the same party + * + * @deprecated This should be useful with the {@link io.lumine.mythic.lib.comp.target.TargetRestriction} update */ + @Deprecated @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) public void b(PlayerAttackEvent event) { LivingEntity entity = event.getEntity(); diff --git a/src/main/java/net/Indyuce/mmocore/listener/option/PlayerProfileCheck.java b/src/main/java/net/Indyuce/mmocore/listener/option/PlayerProfileCheck.java index c0080fc7..86569824 100644 --- a/src/main/java/net/Indyuce/mmocore/listener/option/PlayerProfileCheck.java +++ b/src/main/java/net/Indyuce/mmocore/listener/option/PlayerProfileCheck.java @@ -1,6 +1,7 @@ package net.Indyuce.mmocore.listener.option; import io.lumine.mythic.lib.MythicLib; +import io.lumine.mythic.lib.comp.target.InteractionType; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.manager.InventoryManager; import org.bukkit.entity.EntityType; @@ -13,7 +14,7 @@ public class PlayerProfileCheck implements Listener { @EventHandler public void a(PlayerInteractEntityEvent event) { - if (event.getRightClicked().getType() != EntityType.PLAYER || MythicLib.plugin.getEntities().findCustom(event.getRightClicked())) + if (event.getRightClicked().getType() != EntityType.PLAYER || !MythicLib.plugin.getEntities().canTarget(event.getPlayer(), event.getRightClicked(), InteractionType.SUPPORT_ACTION)) return; /* diff --git a/src/main/java/net/Indyuce/mmocore/skill/list/Greater_Healings.java b/src/main/java/net/Indyuce/mmocore/skill/list/Greater_Healings.java index 5cb3aa3b..a302742f 100644 --- a/src/main/java/net/Indyuce/mmocore/skill/list/Greater_Healings.java +++ b/src/main/java/net/Indyuce/mmocore/skill/list/Greater_Healings.java @@ -1,5 +1,6 @@ package net.Indyuce.mmocore.skill.list; +import io.lumine.mythic.lib.comp.target.InteractionType; import net.Indyuce.mmocore.api.util.math.formula.LinearValue; import net.Indyuce.mmocore.api.util.math.particle.SmallParticleEffect; import net.Indyuce.mmocore.skill.CasterMetadata; @@ -25,7 +26,7 @@ public class Greater_Healings extends Skill { @Override public SkillMetadata whenCast(CasterMetadata caster, SkillInfo skill) { - SkillMetadata cast = caster.getPlayer().isSneaking() ? new SkillMetadata(caster, skill) : new TargetSkillMetadata(caster, skill, 50, true); + SkillMetadata cast = caster.getPlayer().isSneaking() ? new SkillMetadata(caster, skill) : new TargetSkillMetadata(caster, skill, 50, InteractionType.SUPPORT_SKILL); if (!cast.isSuccessful()) return cast; diff --git a/src/main/java/net/Indyuce/mmocore/skill/list/Human_Shield.java b/src/main/java/net/Indyuce/mmocore/skill/list/Human_Shield.java index 4e06d3ad..239b10e0 100644 --- a/src/main/java/net/Indyuce/mmocore/skill/list/Human_Shield.java +++ b/src/main/java/net/Indyuce/mmocore/skill/list/Human_Shield.java @@ -1,5 +1,6 @@ package net.Indyuce.mmocore.skill.list; +import io.lumine.mythic.lib.comp.target.InteractionType; import io.lumine.mythic.lib.version.VersionMaterial; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.player.PlayerData; @@ -35,7 +36,7 @@ public class Human_Shield extends Skill { @Override public SkillMetadata whenCast(CasterMetadata caster, SkillInfo skill) { - TargetSkillMetadata cast = new TargetSkillMetadata(caster, skill, 7, true); + TargetSkillMetadata cast = new TargetSkillMetadata(caster, skill, 7, InteractionType.SUPPORT_SKILL); if (!cast.isSuccessful()) return cast; diff --git a/src/main/java/net/Indyuce/mmocore/skill/list/Ice_Spikes.java b/src/main/java/net/Indyuce/mmocore/skill/list/Ice_Spikes.java index 3f48f22d..a4834bea 100644 --- a/src/main/java/net/Indyuce/mmocore/skill/list/Ice_Spikes.java +++ b/src/main/java/net/Indyuce/mmocore/skill/list/Ice_Spikes.java @@ -20,8 +20,6 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.Vector; -; - public class Ice_Spikes extends Skill { private static final double radius = 3; diff --git a/src/main/java/net/Indyuce/mmocore/skill/list/Minor_Healings.java b/src/main/java/net/Indyuce/mmocore/skill/list/Minor_Healings.java index eff1814d..4bfd9583 100644 --- a/src/main/java/net/Indyuce/mmocore/skill/list/Minor_Healings.java +++ b/src/main/java/net/Indyuce/mmocore/skill/list/Minor_Healings.java @@ -1,5 +1,6 @@ package net.Indyuce.mmocore.skill.list; +import io.lumine.mythic.lib.comp.target.InteractionType; import net.Indyuce.mmocore.api.util.math.formula.LinearValue; import net.Indyuce.mmocore.api.util.math.particle.SmallParticleEffect; import net.Indyuce.mmocore.skill.CasterMetadata; @@ -25,7 +26,7 @@ public class Minor_Healings extends Skill { @Override public SkillMetadata whenCast(CasterMetadata caster, SkillInfo skill) { - SkillMetadata cast = caster.getPlayer().isSneaking() ? new SkillMetadata(caster, skill) : new TargetSkillMetadata(caster, skill, 50, true); + SkillMetadata cast = caster.getPlayer().isSneaking() ? new SkillMetadata(caster, skill) : new TargetSkillMetadata(caster, skill, 50, InteractionType.SUPPORT_SKILL); if (!cast.isSuccessful()) return cast; diff --git a/src/main/java/net/Indyuce/mmocore/skill/metadata/LocationSkillMetadata.java b/src/main/java/net/Indyuce/mmocore/skill/metadata/LocationSkillMetadata.java index d88cdeb1..ca18b901 100644 --- a/src/main/java/net/Indyuce/mmocore/skill/metadata/LocationSkillMetadata.java +++ b/src/main/java/net/Indyuce/mmocore/skill/metadata/LocationSkillMetadata.java @@ -1,5 +1,6 @@ package net.Indyuce.mmocore.skill.metadata; +import io.lumine.mythic.lib.comp.target.InteractionType; import net.Indyuce.mmocore.api.util.MMOCoreUtils; import net.Indyuce.mmocore.skill.CasterMetadata; import net.Indyuce.mmocore.skill.Skill.SkillInfo; @@ -16,23 +17,24 @@ public class LocationSkillMetadata extends SkillMetadata { * @param range Skill raycast range */ public LocationSkillMetadata(CasterMetadata caster, SkillInfo skill, double range) { - this(caster, skill, range, false); + this(caster, skill, range, InteractionType.OFFENSE_SKILL); } /** - * @param caster Player casting the skill - * @param skill Skill being cast - * @param range Skill raycast range - * @param buff If the skill is a buff ie if it can be cast on party members + * @param caster Player casting the skill + * @param skill Skill being cast + * @param range Skill raycast range + * @param interaction If the skill is a friendly or offense skill. This determines if it + * can be cast on party members or not. */ - public LocationSkillMetadata(CasterMetadata caster, SkillInfo skill, double range, boolean buff) { + public LocationSkillMetadata(CasterMetadata caster, SkillInfo skill, double range, InteractionType interaction) { super(caster, skill); if (isSuccessful()) { RayTraceResult result = caster.getPlayer().getWorld().rayTrace(caster.getPlayer().getEyeLocation(), caster.getPlayer().getEyeLocation().getDirection(), range, FluidCollisionMode.ALWAYS, true, 1.0D, - entity -> MMOCoreUtils.canTarget(caster.getPlayerData(), entity, buff)); + entity -> MMOCoreUtils.canTarget(caster.getPlayerData(), entity, interaction)); if (result == null) abort(CancelReason.OTHER); else diff --git a/src/main/java/net/Indyuce/mmocore/skill/metadata/SkillMetadata.java b/src/main/java/net/Indyuce/mmocore/skill/metadata/SkillMetadata.java index 31607ff7..a081c973 100644 --- a/src/main/java/net/Indyuce/mmocore/skill/metadata/SkillMetadata.java +++ b/src/main/java/net/Indyuce/mmocore/skill/metadata/SkillMetadata.java @@ -1,8 +1,8 @@ package net.Indyuce.mmocore.skill.metadata; -import net.Indyuce.mmocore.MMOCore; +import io.lumine.mythic.lib.MythicLib; +import io.lumine.mythic.lib.comp.flags.CustomFlag; import net.Indyuce.mmocore.api.player.PlayerData; -import net.Indyuce.mmocore.comp.flags.FlagPlugin.CustomFlag; import net.Indyuce.mmocore.skill.CasterMetadata; import net.Indyuce.mmocore.skill.Skill; import net.Indyuce.mmocore.skill.Skill.SkillInfo; @@ -30,7 +30,7 @@ public class SkillMetadata { : mana > data.getMana() ? CancelReason.MANA : stamina > data.getStamina() ? CancelReason.STAMINA : !data.isOnline() ? CancelReason.OTHER - : !MMOCore.plugin.flagPlugin.isFlagAllowed(data.getPlayer(), CustomFlag.SKILLS) ? CancelReason.FLAG + : !MythicLib.plugin.getFlags().isFlagAllowed(data.getPlayer(), CustomFlag.MMO_ABILITIES) ? CancelReason.FLAG : null; } diff --git a/src/main/java/net/Indyuce/mmocore/skill/metadata/TargetSkillMetadata.java b/src/main/java/net/Indyuce/mmocore/skill/metadata/TargetSkillMetadata.java index 134e2917..934b2033 100644 --- a/src/main/java/net/Indyuce/mmocore/skill/metadata/TargetSkillMetadata.java +++ b/src/main/java/net/Indyuce/mmocore/skill/metadata/TargetSkillMetadata.java @@ -2,6 +2,7 @@ package net.Indyuce.mmocore.skill.metadata; import io.lumine.mythic.lib.MythicLib; import io.lumine.mythic.lib.api.MMORayTraceResult; +import io.lumine.mythic.lib.comp.target.InteractionType; import net.Indyuce.mmocore.api.util.MMOCoreUtils; import net.Indyuce.mmocore.skill.CasterMetadata; import net.Indyuce.mmocore.skill.Skill.SkillInfo; @@ -16,20 +17,21 @@ public class TargetSkillMetadata extends SkillMetadata { * @param range Skill raycast range */ public TargetSkillMetadata(CasterMetadata caster, SkillInfo skill, double range) { - this(caster, skill, range, false); + this(caster, skill, range, InteractionType.OFFENSE_SKILL); } /** * @param caster Player casting the skill * @param skill Skill being cast * @param range Skill raycast range - * @param buff If the skill is a buff ie if it can be cast on party members + * @param interaction If the skill is a friendly or offense skill. This determines if it + * can be cast on party members or not. */ - public TargetSkillMetadata(CasterMetadata caster, SkillInfo skill, double range, boolean buff) { + public TargetSkillMetadata(CasterMetadata caster, SkillInfo skill, double range, InteractionType interaction) { super(caster, skill); if (isSuccessful()) { - MMORayTraceResult result = MythicLib.plugin.getVersion().getWrapper().rayTrace(caster.getPlayer(), range, entity -> MMOCoreUtils.canTarget(caster.getPlayerData(), entity, buff)); + MMORayTraceResult result = MythicLib.plugin.getVersion().getWrapper().rayTrace(caster.getPlayer(), range, entity -> MMOCoreUtils.canTarget(caster.getPlayerData(), entity, interaction)); if (!result.hasHit()) abort(); else