Added a wilderness teleport command (/f wild) and fixed a few bugs

Signed-off-by: DroppingAnvil <dr0pping.4nvi1@gmail.com>
This commit is contained in:
DroppingAnvil 2019-12-16 18:46:38 -06:00
parent 58e43b41cc
commit fec07b6fac
13 changed files with 300 additions and 18 deletions

View File

@ -261,7 +261,7 @@ public interface FPlayer extends EconomyParticipator {
int getDeaths(); int getDeaths();
void takeMoney(int amt); boolean takeMoney(int amt);
boolean hasMoney(int amt); boolean hasMoney(int amt);

View File

@ -34,12 +34,10 @@ public class CmdBanner extends FCommand {
context.msg(TL.COMMAND_BANNER_NOBANNER); context.msg(TL.COMMAND_BANNER_NOBANNER);
return; return;
} }
if (!context.fPlayer.hasMoney(FactionsPlugin.getInstance().getConfig().getInt("fbanners.Banner-Cost", 5000))) { if (!context.fPlayer.takeMoney(FactionsPlugin.getInstance().getConfig().getInt("fbanners.Banner-Cost", 5000))) {
context.msg(TL.COMMAND_BANNER_NOTENOUGHMONEY); context.msg(TL.COMMAND_BANNER_NOTENOUGHMONEY);
return; return;
} }
this.takeMoney(context.fPlayer, FactionsPlugin.getInstance().getConfig().getInt("fbanners.Banner-Cost", 5000));
this.takeMoney(context.fPlayer, FactionsPlugin.getInstance().getConfig().getInt("fbanners.Banner-Cost", 5000));
ItemStack warBanner = context.fPlayer.getFaction().getBanner(); ItemStack warBanner = context.fPlayer.getFaction().getBanner();
ItemMeta warmeta = warBanner.getItemMeta(); ItemMeta warmeta = warBanner.getItemMeta();
warmeta.setDisplayName(FactionsPlugin.getInstance().color(FactionsPlugin.getInstance().getConfig().getString("fbanners.Item.Name"))); warmeta.setDisplayName(FactionsPlugin.getInstance().color(FactionsPlugin.getInstance().getConfig().getString("fbanners.Item.Name")));
@ -49,7 +47,7 @@ public class CmdBanner extends FCommand {
warBanner.setAmount(1); warBanner.setAmount(1);
context.player.getInventory().addItem(warBanner); context.player.getInventory().addItem(warBanner);
} }
@Deprecated
public boolean hasMoney(FPlayer fme, int amt) { public boolean hasMoney(FPlayer fme, int amt) {
Economy econ = FactionsPlugin.getInstance().getEcon(); Economy econ = FactionsPlugin.getInstance().getEcon();
if (econ.getBalance(fme.getPlayer()) >= amt) { if (econ.getBalance(fme.getPlayer()) >= amt) {
@ -58,11 +56,10 @@ public class CmdBanner extends FCommand {
fme.msg(TL.COMMAND_BANNER_NOTENOUGHMONEY); fme.msg(TL.COMMAND_BANNER_NOTENOUGHMONEY);
return false; return false;
} }
@Deprecated
public void takeMoney(FPlayer fme, int amt) { public void takeMoney(FPlayer fme, int amt) {
if (this.hasMoney(fme, amt)) { if (this.hasMoney(fme, amt)) {
Economy econ = FactionsPlugin.getInstance().getEcon(); Economy econ = FactionsPlugin.getInstance().getEcon();
econ.withdrawPlayer(fme.getPlayer(), (double) amt);
fme.sendMessage(TL.COMMAND_BANNER_MONEYTAKE.toString().replace("{amount}", amt + "")); fme.sendMessage(TL.COMMAND_BANNER_MONEYTAKE.toString().replace("{amount}", amt + ""));
} }
} }

View File

@ -47,9 +47,9 @@ public class CmdGetVault extends FCommand {
return; return;
} }
if (!context.fPlayer.takeMoney(amount)) {return;}
//success :) //success :)
context.fPlayer.takeMoney(amount);
context.player.getInventory().addItem(vault); context.player.getInventory().addItem(vault);
context.fPlayer.msg(TL.COMMAND_GETVAULT_RECEIVE); context.fPlayer.msg(TL.COMMAND_GETVAULT_RECEIVE);

View File

@ -20,6 +20,7 @@ import com.massivecraft.factions.cmd.roles.CmdDemote;
import com.massivecraft.factions.cmd.roles.CmdPromote; import com.massivecraft.factions.cmd.roles.CmdPromote;
import com.massivecraft.factions.cmd.tnt.CmdTnt; import com.massivecraft.factions.cmd.tnt.CmdTnt;
import com.massivecraft.factions.cmd.tnt.CmdTntFill; import com.massivecraft.factions.cmd.tnt.CmdTntFill;
import com.massivecraft.factions.cmd.wild.CmdWild;
import com.massivecraft.factions.discord.CmdInviteBot; import com.massivecraft.factions.discord.CmdInviteBot;
import com.massivecraft.factions.discord.CmdSetGuild; import com.massivecraft.factions.discord.CmdSetGuild;
import com.massivecraft.factions.missions.CmdMissions; import com.massivecraft.factions.missions.CmdMissions;
@ -152,6 +153,7 @@ public class FCmdRoot extends FCommand implements CommandExecutor {
public CmdStrikes cmdStrikes = new CmdStrikes(); public CmdStrikes cmdStrikes = new CmdStrikes();
public CmdCheck cmdCheck = new CmdCheck(); public CmdCheck cmdCheck = new CmdCheck();
public CmdWeeWoo cmdWeeWoo = new CmdWeeWoo(); public CmdWeeWoo cmdWeeWoo = new CmdWeeWoo();
public CmdWild cmdWild = new CmdWild();
public CmdConvertConfig cmdConvertConfig = new CmdConvertConfig(); public CmdConvertConfig cmdConvertConfig = new CmdConvertConfig();
public CmdSpawnerLock cmdSpawnerLock = new CmdSpawnerLock(); public CmdSpawnerLock cmdSpawnerLock = new CmdSpawnerLock();
public CmdSetDiscord cmdSetDiscord = new CmdSetDiscord(); public CmdSetDiscord cmdSetDiscord = new CmdSetDiscord();
@ -174,6 +176,7 @@ public class FCmdRoot extends FCommand implements CommandExecutor {
public Boolean fPayPalEnabled = false; public Boolean fPayPalEnabled = false;
public Boolean coreProtectEnabled = false; public Boolean coreProtectEnabled = false;
public Boolean internalFTOPEnabled = false; public Boolean internalFTOPEnabled = false;
public Boolean fWildEnabled = false;
public FCmdRoot() { public FCmdRoot() {
super(); super();
@ -334,6 +337,10 @@ public class FCmdRoot extends FCommand implements CommandExecutor {
internalFTOPEnabled = true; internalFTOPEnabled = true;
} }
//Other //Other
if (FactionsPlugin.getInstance().getConfig().getBoolean("Wild.Enabled", false) && !fWildEnabled) {
this.addSubCommand(this.cmdWild);
fWildEnabled = true;
}
if (FactionsPlugin.getInstance().getConfig().getBoolean("Missions-Enabled", false) && !missionsEnabled) { if (FactionsPlugin.getInstance().getConfig().getBoolean("Missions-Enabled", false) && !missionsEnabled) {
this.addSubCommand(this.cmdMissions); this.addSubCommand(this.cmdMissions);
missionsEnabled = true; missionsEnabled = true;

View File

@ -0,0 +1,110 @@
package com.massivecraft.factions.cmd.wild;
import com.massivecraft.factions.Board;
import com.massivecraft.factions.FLocation;
import com.massivecraft.factions.FPlayers;
import com.massivecraft.factions.FactionsPlugin;
import com.massivecraft.factions.cmd.CommandContext;
import com.massivecraft.factions.cmd.CommandRequirements;
import com.massivecraft.factions.cmd.FCommand;
import com.massivecraft.factions.struct.Permission;
import com.massivecraft.factions.zcore.util.TL;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Random;
public class CmdWild extends FCommand {
public static HashMap<Player, Integer> waitingTeleport;
public static HashMap<Player, String> teleportRange;
public static HashSet<Player> teleporting;
public CmdWild() {
super();
this.aliases.add("wild");
this.requirements = new CommandRequirements.Builder(Permission.WILD)
.playerOnly()
.build();
waitingTeleport = new HashMap<>();
teleporting = new HashSet<>();
teleportRange = new HashMap<>();
startWild();
}
@Override
public void perform(CommandContext context) {
if (!waitingTeleport.keySet().contains(context.player)) {
context.player.openInventory(new WildGUI(context.player, context.fPlayer).getInventory());
} else {context.fPlayer.msg(TL.COMMAND_WILD_WAIT);}
}
public void startWild() {
Bukkit.getScheduler().scheduleSyncRepeatingTask(FactionsPlugin.instance, () -> {
for (Player p : waitingTeleport.keySet()) {
int i = waitingTeleport.get(p) - 1;
if (i > 0) {
if (i != 1) {
p.sendMessage(TL.COMMAND_WILD_WAIT.format((i + " Seconds")));
} else {
p.sendMessage(TL.COMMAND_WILD_WAIT.format((i + " Second")));
}
waitingTeleport.replace(p, i);
} else {
p.sendMessage(TL.COMMAND_WILD_SUCCESS.toString());
waitingTeleport.remove(p);
attemptTeleport(p);
}
}
}, 0L, 20L);
}
public void attemptTeleport(Player p) {
boolean success = false;
int tries = 0;
ConfigurationSection c = FactionsPlugin.getInstance().getConfig().getConfigurationSection("Wild.Zones." + teleportRange.get(p));
while (tries < 5) {
int x = new Random().nextInt((c.getInt("Range.MaxX") - c.getInt("Range.MinX")) + 1) + c.getInt("Range.MinX");
int z = new Random().nextInt((c.getInt("Range.MaxZ") - c.getInt("Range.MinZ")) + 1) + c.getInt("Range.MinZ");
if (Board.getInstance().getFactionAt(new FLocation(p.getWorld().getName(), x, z)).isWilderness()) {
success = true;
FLocation loc = new FLocation(p.getWorld().getName(), x, z);
teleportRange.remove(p);
if (!FPlayers.getInstance().getByPlayer(p).takeMoney(c.getInt("Cost"))) {
p.sendMessage(TL.GENERIC_NOTENOUGHMONEY.toString());
return;
}
teleportPlayer(p, loc);
break;
}
tries++;
}
if (!success) {p.sendMessage(TL.COMMAND_WILD_FAILED.toString());}
}
public void teleportPlayer(Player p, FLocation loc) {
Location finalLoc;
if (FactionsPlugin.getInstance().getConfig().getBoolean("Wild.Arrival.SpawnAbove")) {
finalLoc = new Location(p.getWorld(), loc.getX(), p.getWorld().getHighestBlockYAt(Math.round(loc.getX()), Math.round(loc.getZ())) + FactionsPlugin.getInstance().getConfig().getInt("Wild.Arrival.SpawnAboveBlocks", 1), loc.getZ());
} else {finalLoc = new Location(p.getWorld(), loc.getX(), p.getWorld().getHighestBlockYAt(Math.round(loc.getX()), Math.round(loc.getZ())), loc.getZ());}
p.teleport(finalLoc, PlayerTeleportEvent.TeleportCause.PLUGIN);
setTeleporting(p);
applyEffects(p);
}
public void applyEffects(Player p) {
for (String s : FactionsPlugin.getInstance().getConfig().getStringList("Wild.Arrival.Effects")) {
p.addPotionEffect(new PotionEffect(PotionEffectType.getByName(s), 40, 1));
}
}
public void setTeleporting(Player p) {
teleporting.add(p);
Bukkit.getScheduler().scheduleSyncDelayedTask(FactionsPlugin.instance, () -> teleporting.remove(p), FactionsPlugin.getInstance().getConfig().getInt("Wild.Arrival.FallDamageWindow") * 20);
}
@Override
public TL getUsageTranslation() {
return TL.COMMAND_WILD_DESCRIPTION;
}
}

View File

@ -0,0 +1,74 @@
package com.massivecraft.factions.cmd.wild;
import com.massivecraft.factions.FPlayer;
import com.massivecraft.factions.FactionsPlugin;
import com.massivecraft.factions.integration.Essentials;
import com.massivecraft.factions.util.FactionGUI;
import com.massivecraft.factions.util.XMaterial;
import com.massivecraft.factions.zcore.util.TL;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class WildGUI implements FactionGUI {
Player player;
FPlayer fplayer;
HashMap<Integer, String> map;
Inventory inv;
public WildGUI(Player player, FPlayer fplayer) {
this.player = player;
this.fplayer = fplayer;
map = new HashMap<>();
}
@Override
public void onClick(int slot, ClickType action) {
if (map.keySet().contains(slot)) {
String zone = map.get(slot);
if (fplayer.hasMoney(FactionsPlugin.getInstance().getConfig().getInt("Wild.Zones." + zone + ".Cost"))) {
CmdWild.waitingTeleport.put(player, FactionsPlugin.getInstance().getConfig().getInt("Wild.Wait"));
CmdWild.teleportRange.put(player, zone);
fplayer.msg(TL.COMMAND_WILD_WAIT, FactionsPlugin.getInstance().getConfig().getInt("Wild.Wait") + " Seconds");
player.closeInventory();
}
}
}
@Override
public void build() {
inv = Bukkit.createInventory(this, FactionsPlugin.getInstance().getConfig().getInt("Wild.GUI.Size"), FactionsPlugin.getInstance().color(FactionsPlugin.getInstance().getConfig().getString("Wild.GUI.Name")));
ItemStack fillItem = XMaterial.matchXMaterial(FactionsPlugin.getInstance().getConfig().getString("Wild.GUI.FillMaterial")).parseItem();
ItemMeta meta = fillItem.getItemMeta();
meta.setDisplayName("");
fillItem.setItemMeta(meta);
for (int fill = 0; fill < FactionsPlugin.getInstance().getConfig().getInt("Wild.GUI.Size"); ++fill) {
inv.setItem(fill, fillItem);
}
for (String key : FactionsPlugin.getInstance().getConfig().getConfigurationSection("Wild.Zones").getKeys(false)) {
ItemStack zoneItem = XMaterial.matchXMaterial(FactionsPlugin.getInstance().getConfig().getString("Wild.Zones." + key + ".Material")).parseItem();
ItemMeta zoneMeta = zoneItem.getItemMeta();
List<String> lore = new ArrayList<>();
for (String s : FactionsPlugin.getInstance().getConfig().getStringList("Wild.Zones." + key + ".Lore")) {
lore.add(FactionsPlugin.getInstance().color(s));
}
zoneMeta.setLore(lore);
zoneMeta.setDisplayName(FactionsPlugin.getInstance().color(FactionsPlugin.getInstance().getConfig().getString("Wild.Zones." + key + ".Name")));
zoneItem.setItemMeta(zoneMeta);
int slot = FactionsPlugin.getInstance().getConfig().getInt("Wild.Zones." + key + ".Slot");
map.put(slot, key);
inv.setItem(slot, zoneItem);
}
}
@Override
public Inventory getInventory() {
if (inv == null) {build();}
return inv;
}
}

View File

@ -5,6 +5,7 @@ import com.massivecraft.factions.cmd.CmdFGlobal;
import com.massivecraft.factions.cmd.CmdFly; import com.massivecraft.factions.cmd.CmdFly;
import com.massivecraft.factions.cmd.CmdSeeChunk; import com.massivecraft.factions.cmd.CmdSeeChunk;
import com.massivecraft.factions.cmd.logout.LogoutHandler; import com.massivecraft.factions.cmd.logout.LogoutHandler;
import com.massivecraft.factions.cmd.wild.CmdWild;
import com.massivecraft.factions.discord.Discord; import com.massivecraft.factions.discord.Discord;
import com.massivecraft.factions.event.FPlayerEnteredFactionEvent; import com.massivecraft.factions.event.FPlayerEnteredFactionEvent;
import com.massivecraft.factions.event.FPlayerJoinEvent; import com.massivecraft.factions.event.FPlayerJoinEvent;
@ -924,6 +925,10 @@ public class FactionsPlayerListener implements Listener {
handler.cancelLogout(e.getPlayer()); handler.cancelLogout(e.getPlayer());
e.getPlayer().sendMessage(String.valueOf(TL.COMMAND_LOGOUT_MOVED)); e.getPlayer().sendMessage(String.valueOf(TL.COMMAND_LOGOUT_MOVED));
} }
if (CmdWild.waitingTeleport.containsKey(e.getPlayer())) {
CmdWild.waitingTeleport.remove(e.getPlayer());
FPlayers.getInstance().getByPlayer(e.getPlayer()).msg(TL.COMMAND_WILD_INTERUPTED);
}
} }
@EventHandler @EventHandler
@ -935,6 +940,15 @@ public class FactionsPlayerListener implements Listener {
handler.cancelLogout(player); handler.cancelLogout(player);
player.sendMessage(String.valueOf(TL.COMMAND_LOGOUT_DAMAGE_TAKEN)); player.sendMessage(String.valueOf(TL.COMMAND_LOGOUT_DAMAGE_TAKEN));
} }
if (CmdWild.waitingTeleport.containsKey(player)) {
CmdWild.waitingTeleport.remove(player);
FPlayers.getInstance().getByPlayer(player).msg(TL.COMMAND_WILD_INTERUPTED);
}
if (CmdWild.teleporting.contains(player)) {
if (!FactionsPlugin.getInstance().getConfig().getBoolean("Wild.FallDamage") && e.getCause() == EntityDamageEvent.DamageCause.FALL) {
e.setCancelled(true);
}
}
} }
} }

View File

@ -96,11 +96,12 @@ public class MissionGUI implements FactionGUI {
if (configurationSection == null) { if (configurationSection == null) {
return; return;
} }
ItemStack fillItem = XMaterial.matchXMaterial(configurationSection.getString("FillItem.Material")).parseItem();
ItemMeta fillmeta = fillItem.getItemMeta();
fillmeta.setDisplayName("");
fillItem.setItemMeta(fillmeta);
for (int fill = 0; fill < configurationSection.getInt("FillItem.Rows") * 9; ++fill) { for (int fill = 0; fill < configurationSection.getInt("FillItem.Rows") * 9; ++fill) {
ItemStack fillItem = new ItemStack(XMaterial.matchXMaterial(configurationSection.getString("FillItem.Material")).parseItem()); //Why were we generating a new itemstack per slot?????
ItemMeta meta = fillItem.getItemMeta();
meta.setDisplayName("");
fillItem.setItemMeta(meta);
inventory.setItem(fill, fillItem); inventory.setItem(fill, fillItem);
} }
for (String key : configurationSection.getKeys(false)) { for (String key : configurationSection.getKeys(false)) {

View File

@ -146,6 +146,7 @@ public enum Permission {
COORD("coords"), COORD("coords"),
SHOWCLAIMS("showclaims"), SHOWCLAIMS("showclaims"),
WARP("warp"), WARP("warp"),
WILD("wild"),
CHEST("chest"); CHEST("chest");
public final String node; public final String node;

View File

@ -651,13 +651,16 @@ public class FUpgradesGUI implements Listener {
return fme.hasMoney(amt); return fme.hasMoney(amt);
} }
private void takeMoney(FPlayer fme, int amt) { private boolean takeMoney(FPlayer fme, int amt) {
fme.takeMoney(amt); if (fme.takeMoney(amt)) {
return true;
}
return false;
} }
private boolean upgradeItem(FPlayer fme, UpgradeType upgrade, int level, int cost) { private boolean upgradeItem(FPlayer fme, UpgradeType upgrade, int level, int cost) {
if (hasMoney(fme, cost)) { if (hasMoney(fme, cost)) {
takeMoney(fme, cost); if (!takeMoney(fme, cost)) {return false;}
fme.getFaction().setUpgrade(upgrade, level); fme.getFaction().setUpgrade(upgrade, level);
fme.getPlayer().closeInventory(); fme.getPlayer().closeInventory();
return true; return true;

View File

@ -1376,11 +1376,14 @@ public abstract class MemoryFPlayer implements FPlayer {
} }
@Override @Override
public void takeMoney(int amt) { public boolean takeMoney(int amt) {
if (hasMoney(amt)) { if (hasMoney(amt)) {
Economy econ = FactionsPlugin.getInstance().getEcon(); Economy econ = FactionsPlugin.getInstance().getEcon();
econ.withdrawPlayer(getPlayer(), amt); if (econ.withdrawPlayer(getPlayer(), amt).transactionSuccess()) {
sendMessage(TL.GENERIC_MONEYTAKE.toString().replace("{amount}", commas(amt))); sendMessage(TL.GENERIC_MONEYTAKE.toString().replace("{amount}", commas(amt)));
return true;
} }
} }
return false;
}
} }

View File

@ -959,6 +959,13 @@ public enum TL {
COMMAND_WARUNCLAIMALL_SUCCESS("You unclaimed ALL war zone land."), COMMAND_WARUNCLAIMALL_SUCCESS("You unclaimed ALL war zone land."),
COMMAND_WARUNCLAIMALL_LOG("%1$s unclaimed all war zones."), COMMAND_WARUNCLAIMALL_LOG("%1$s unclaimed all war zones."),
COMMAND_WILD_DESCRIPTION("Teleport to a random location"),
COMMAND_WILD_WAIT("&c&l[!] &7Teleporting in %1$s"),
COMMAND_WILD_SUCCESS("&c&l[!] &7Teleporting..."),
COMMAND_WILD_INTERUPTED("&c&l[!] &7Teleport cancelled!"),
COMMAND_WILD_FAILED("&c&l[!] &7Could not find a location to teleport you to!"),
COMMAND_WILD_INPROGRESS("&c&l[!] &7You are already teleporting somewhere!"),
COMMAND_RULES_DISABLED_MSG("&cThis command is disabled!"), COMMAND_RULES_DISABLED_MSG("&cThis command is disabled!"),
COMMAND_RULES_DESCRIPTION("set/remove/add rules!"), COMMAND_RULES_DESCRIPTION("set/remove/add rules!"),
COMMAND_RULES_ADD_INVALIDARGS("Please include a rule!"), COMMAND_RULES_ADD_INVALIDARGS("Please include a rule!"),

View File

@ -1330,6 +1330,71 @@ Tntfill:
enabled: true enabled: true
max-radius: 32 max-radius: 32
max-amount: 64 max-amount: 64
Wild:
Enabled: true
# Time to wait in seconds #
Wait: 5
# General GUI Settings #
GUI:
Name: 'Teleporter'
Size: 9
# Should the GUI fill empty spaces? #
Fill: true
FillMaterial: BLACK_STAINED_GLASS_PANE
Zones:
# You may create your own zones here please just follow the original format #
Close:
Range:
MinX: -200
MaxX: 200
MinZ: -200
MaxZ: 200
Cost: 5000
Material: IRON_INGOT
Lore:
- '&eTeleport to a random location close by'
- '&e X &b-200 &f- &b200 &eZ &b-200 &f- &b200'
- '&2&l&o$5000'
Name: '&cLow Range'
Slot: 1
Medium:
Range:
MinX: -400
MaxX: 400
MinZ: -400
MaxZ: 400
Cost: 10000
Material: GOLD_INGOT
Lore:
- '&eTeleport to a random location in a medium proximity'
- '&e X &b-400 &f- &b400 &eZ &b-400 &f- &b400'
- '&2&l&o$10000'
Name: '&cMedium Range'
Slot: 4
Far:
Range:
MinX: -800
MaxX: 800
MinZ: -800
MaxZ: 800
Cost: 15000
Material: DIAMOND
Lore:
- '&eTeleport to a random location far away'
- '&e X &b-800 &f- &b800 &eZ &b-800 &f- &b800'
- '&2&l&o$15000'
Name: '&cHigh Range'
Slot: 7
# Settings that change how a player arrives to their random location #
Arrival:
# if FallDamage is false and the player is about to take fall damage while in the FallDamageWindow it will be denied #
FallDamage: false
# Window to revert fall damage in seconds #
FallDamageWindow: 2
SpawnAbove: true
SpawnAboveBlocks: 10
Effects:
- 'NIGHT_VISION'
############################################################ ############################################################
# +------------------------------------------------------+ # # +------------------------------------------------------+ #