diff --git a/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilItem.java b/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilItem.java index 22a7f4a7e..dc970e4eb 100644 --- a/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilItem.java +++ b/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilItem.java @@ -878,6 +878,16 @@ public class UtilItem return isLeaf(stack == null ? null : stack.getType()); } + public static boolean isDoor(Material type) + { + return type == null ? false : (contains(type, ItemCategory.DOOR)); + } + + public static boolean isDoor(ItemStack stack) + { + return isDoor(stack == null ? null : stack.getType()); + } + public static boolean isTool(Material material) { return material == null ? false : (contains(material, ItemCategory.TOOL)); diff --git a/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilText.java b/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilText.java index 5c289d176..fff061956 100644 --- a/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilText.java +++ b/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilText.java @@ -698,19 +698,4 @@ public class UtilText return possesiveNoun.endsWith("s") ? possesiveNoun + "' " + noun : possesiveNoun + "'s " + noun; } - - public static String colorPercentage(int percentage) - { - Map colors = new HashMap<>(); - - colors.put(Integer.valueOf(100), C.cGreen); - colors.put(Integer.valueOf(80), C.cDGreen); - colors.put(Integer.valueOf(60), C.cYellow); - colors.put(Integer.valueOf(40), C.cGold); - colors.put(Integer.valueOf(20), C.cRed); - colors.put(Integer.valueOf(0), C.cDRed); - - return colors.get(Integer.valueOf(percentage / colors.size())); - } - } \ No newline at end of file diff --git a/Plugins/Mineplex.Core/src/mineplex/core/stats/StatsManager.java b/Plugins/Mineplex.Core/src/mineplex/core/stats/StatsManager.java index ae88b6cb1..8c2bc5e06 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/stats/StatsManager.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/stats/StatsManager.java @@ -294,6 +294,8 @@ public class StatsManager extends MiniDbClientPlugin }); } + + public int getStatId(String statName) { return _stats.get(statName); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java index 81891c6b7..ff4aa3648 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java @@ -383,7 +383,7 @@ public class ClansManager extends MiniClientPluginimplements IRelati Bukkit.getMessenger().registerOutgoingPluginChannel(plugin, "Replay|Restrict"); - new SiegeManager(plugin, this); + new SiegeManager(this); } @Override diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/SiegeManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/SiegeManager.java index bd4b3a8e0..8c05ae2f2 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/SiegeManager.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/SiegeManager.java @@ -1,7 +1,6 @@ package mineplex.game.clans.clans.siege; -import java.util.HashMap; -import java.util.Map; +import java.util.Stack; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -9,12 +8,12 @@ import org.bukkit.entity.Projectile; import org.bukkit.event.EventHandler; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.ProjectileHitEvent; -import org.bukkit.plugin.java.JavaPlugin; import com.google.gson.Gson; import mineplex.core.MiniPlugin; import mineplex.core.common.util.F; +import mineplex.core.common.util.NautHashMap; import mineplex.core.common.util.UtilInv; import mineplex.core.common.util.UtilMath; import mineplex.core.common.util.UtilPlayer; @@ -26,6 +25,8 @@ import mineplex.game.clans.clans.ClansManager; import mineplex.game.clans.clans.siege.commands.CommandSiegeSupplies; import mineplex.game.clans.clans.siege.outpost.Outpost; import mineplex.game.clans.clans.siege.outpost.OutpostManager; +import mineplex.game.clans.clans.siege.repository.SiegeWeaponRepository; +import mineplex.game.clans.clans.siege.repository.tokens.SiegeWeaponToken; import mineplex.game.clans.clans.siege.weapon.Cannon; import mineplex.game.clans.clans.siege.weapon.Catapult; import mineplex.game.clans.clans.siege.weapon.SiegeWeapon; @@ -33,23 +34,25 @@ import mineplex.game.clans.core.repository.ClanTerritory; public class SiegeManager extends MiniPlugin { - private ClansManager _clans; + private ClansManager _clansManager; private OutpostManager _outpostManager; public static SiegeManager Instance; - public Map LiveSiegeWeapons = new HashMap<>(); + public NautHashMap LiveSiegeWeapons = new NautHashMap<>(); private Gson _gson; - public SiegeManager(JavaPlugin plugin, ClansManager clans) + private SiegeWeaponRepository _repository; + + public SiegeManager(ClansManager clans) { - super("Siege", plugin); + super("Siege", clans.getPlugin()); _gson = new Gson(); - _clans = clans; + _clansManager = clans; _outpostManager = new OutpostManager(clans, this); @@ -57,8 +60,11 @@ public class SiegeManager extends MiniPlugin Instance = this; - _outpostManager.loadOutposts(); - loadSiegeWeapons(); + _repository = new SiegeWeaponRepository(clans.getPlugin(), this); + + _outpostManager.loadOutposts(() -> { + loadSiegeWeapons(); + }); } @EventHandler @@ -66,19 +72,96 @@ public class SiegeManager extends MiniPlugin { if (event.getType() == UpdateType.SLOWER) { - _outpostManager.saveOutposts(); - saveSiegeWeapons(); + _outpostManager.saveOutposts(() -> { + saveSiegeWeapons(); + }); } } private void loadSiegeWeapons() { - + _repository.getWeaponsByServer(_clansManager.getServerId(), tokens -> + tokens.forEach(token -> { + SiegeWeapon weapon = null; + + switch(token.WeaponType) + { + case 1: + weapon = new Catapult(this, token); + break; + case 2: + weapon = new Cannon(this, token); + break; + default: + System.out.println("[WEAPONS] ERROR WHILST LOADING WEAPON: INVALID WEAPON TYPE"); + return; + } + + if (!weapon.getLocation().getChunk().load()) + { + System.out.println("[WEAPONS] SKIPPING & REMOVING WEAPON [" + token.UniqueId + "] BECAUSE OF CHUNK LOADING FAILURE"); + + weapon.kill(); + + _repository.deleteWeapon(token.UniqueId); + return; + } + + if (token.OutpostId != 0) + { + Outpost outpost = _outpostManager.Get(token.OutpostId); + + if (outpost == null) + { + System.out.println("[WEAPONS] SKIPPING & REMOVING WEAPON [" + token.UniqueId + "] BECAUSE OF PARENT OUTPOST DEATH"); + + weapon.kill(); + + _repository.deleteWeapon(token.UniqueId); + + return; + } + + outpost.addWeapon(weapon); + } + + System.out.println("[WEAPONS] LOADED SIEGE WEAPON " + weapon.getClass().getSimpleName() + " [" + token.UniqueId + "]"); + + LiveSiegeWeapons.put(Integer.valueOf(token.UniqueId), weapon); + }) + ); } private void saveSiegeWeapons() { + final Stack queue = new Stack<>(); + for (final SiegeWeapon weapon : LiveSiegeWeapons.values()) + { + final SiegeWeaponToken token = weapon.toToken(); + + queue.push(() -> { + _repository.updateWeapon(token); + }); + } + + runAsync(() -> { + while (!queue.isEmpty()) + { + queue.pop().run(); + } + }); + + runAsync(() -> + _repository.getWeaponsByServer(_clansManager.getServerId(), tokens -> + tokens.forEach(token -> { + if (!LiveSiegeWeapons.containsKey(Integer.valueOf(token.UniqueId))) + { + _repository.deleteWeapon(token.UniqueId); + } + }) + ) + ); } @EventHandler @@ -90,13 +173,13 @@ public class SiegeManager extends MiniPlugin { String[] data = projectile.getMetadata("OutpostData").get(0).asString().split(";"); - ClanInfo clan = _clans.getClanUtility().getClanByClanName(data[0]); + ClanInfo clan = _clansManager.getClanUtility().getClanByClanName(data[0]); Player player = UtilPlayer.searchExact(data[1]); if (_outpostManager.Get(clan) != null && player != null) { - ClanTerritory territory = _clans.getClanUtility().getClaim(projectile.getLocation()); + ClanTerritory territory = _clansManager.getClanUtility().getClaim(projectile.getLocation()); if (territory != null && ClansBlacklist.isValidClanName(territory.Owner) && !territory.Owner.equals(clan.getName())) { @@ -107,7 +190,7 @@ public class SiegeManager extends MiniPlugin } else { - _outpostManager.Get(clan).declareOn(player, _clans.getClanUtility().getClanByClanName(territory.Owner)); + _outpostManager.Get(clan).declareOn(player, _clansManager.getClanUtility().getClanByClanName(territory.Owner)); } } else @@ -151,18 +234,18 @@ public class SiegeManager extends MiniPlugin public boolean trySpawnCannon(Player player, Location location) { - if (!_clans.isInClan(player)) + if (!_clansManager.isInClan(player)) { UtilPlayer.message(player, F.main("Clans", "You must be in a Clan to place a Cannon.")); return false; } Outpost placedOutpost = null; - ClanInfo clan = _clans.getClanUtility().getClanByPlayer(player); + ClanInfo clan = _clansManager.getClanUtility().getClanByPlayer(player); - if (_clans.getClanUtility().isClaimed(location)) + if (_clansManager.getClanUtility().isClaimed(location)) { - if (!_clans.getClanUtility().getClaim(location).Owner.equals(clan.getName())) + if (!_clansManager.getClanUtility().getClaim(location).Owner.equals(clan.getName())) { UtilPlayer.message(player, F.main("Clans", "A Cannon must be placed in your own territory or your Outpost in the case of a Siege.")); return false; @@ -203,30 +286,30 @@ public class SiegeManager extends MiniPlugin public void spawnCannon(Player player, Location location, Outpost outpost) { - Cannon cannon = new Cannon(location, _clans.getClan(player), this, outpost); + Cannon cannon = new Cannon(location, _clansManager.getClan(player), this, outpost); if (outpost != null) { outpost.addWeapon(cannon); } - LiveSiegeWeapons.put(cannon.getUniqueId(), cannon); + LiveSiegeWeapons.put(Integer.valueOf(cannon.getUniqueId()), cannon); } public boolean trySpawnCatapult(Player player, Location location) { - if (!_clans.isInClan(player)) + if (!_clansManager.isInClan(player)) { UtilPlayer.message(player, F.main("Clans", "You must be in a Clan to place a CanCatapultnon.")); return false; } - ClanInfo clan = _clans.getClanUtility().getClanByPlayer(player); + ClanInfo clan = _clansManager.getClanUtility().getClanByPlayer(player); Outpost placedOutpost = null; - if (_clans.getClanUtility().isClaimed(location)) + if (_clansManager.getClanUtility().isClaimed(location)) { - if (!_clans.getClanUtility().getClaim(location).Owner.equals(clan.getName())) + if (!_clansManager.getClanUtility().getClaim(location).Owner.equals(clan.getName())) { UtilPlayer.message(player, F.main("Clans", "A Catapult must be placed in your own territory or your Outpost in the case of a Siege.")); return false; @@ -266,19 +349,21 @@ public class SiegeManager extends MiniPlugin public void spawnCatapult(Player player, Location location, Outpost outpost) { - Catapult catapult = new Catapult(location, _clans.getClan(player), this, outpost); + Catapult catapult = new Catapult(location, _clansManager.getClan(player), this, outpost); if (outpost != null) { outpost.addWeapon(catapult); } - LiveSiegeWeapons.put(catapult.getUniqueId(), catapult); + LiveSiegeWeapons.put(Integer.valueOf(catapult.getUniqueId()), catapult); } public void dead(SiegeWeapon weapon) { - LiveSiegeWeapons.remove(weapon.getUniqueId()); + LiveSiegeWeapons.remove(Integer.valueOf(weapon.getUniqueId())); + + _repository.deleteWeapon(weapon.getUniqueId()); } public OutpostManager getOutpostManager() @@ -293,12 +378,20 @@ public class SiegeManager extends MiniPlugin public ClansManager getClansManager() { - return _clans; + return _clansManager; } public int randomId() { - return Math.abs(UtilMath.random.nextInt()); + /** + * prevents id from ever being 0 (which is used internally as NULL) + */ + return 1 + UtilMath.random.nextInt(Integer.MAX_VALUE - 1); + } + + public SiegeWeaponRepository getRepository() + { + return _repository; } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/outpost/Outpost.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/outpost/Outpost.java index fed50eb70..545a9c329 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/outpost/Outpost.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/outpost/Outpost.java @@ -17,6 +17,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockDamageEvent; import org.bukkit.event.block.BlockPlaceEvent; @@ -59,6 +60,7 @@ import mineplex.core.updater.UpdateType; import mineplex.core.updater.event.UpdateEvent; import mineplex.game.clans.clans.ClanInfo; import mineplex.game.clans.clans.ClansBlacklist; +import mineplex.game.clans.clans.event.PlayerClaimTerritoryEvent; import mineplex.game.clans.clans.siege.events.SiegeWeaponExplodeEvent; import mineplex.game.clans.clans.siege.outpost.build.OutpostBlock; import mineplex.game.clans.clans.siege.repository.tokens.OutpostToken; @@ -124,6 +126,8 @@ public class Outpost implements Listener private long _siegeDeclaredTime = -1; private Arrow _declarationArrow; + private List _nameHolograms; + public Outpost(OutpostManager outpostManager, OutpostToken token) { _outpostManager = outpostManager; @@ -141,6 +145,8 @@ public class Outpost implements Listener _ownerClan = token.OwnerClan; + _nameHolograms = new ArrayList<>(); + _startCorner = token.Origin.clone().subtract(token.Type._size, 1.1, token.Type._size); _endCorner = token.Origin.clone().add(token.Type._size + .9, token.Type._ySize - 1, token.Type._size + .9); @@ -157,8 +163,8 @@ public class Outpost implements Listener _core = _type.getCoreLocation(_origin); - _preHologram = new Hologram(_ownerClan.Clans.getHologramManager(), _origin.clone().add(0.5, 2.3, 0.5), F.elem(_ownerClan.getName()) + C.cWhite + "'s Outpost block (Right-Click to activate)"); - _preHologram2 = new Hologram(_ownerClan.Clans.getHologramManager(), _origin.clone().add(0.5, 3, 0.5), "Despawning: " + UtilText.getProgress(null, 0, null, true)); + _preHologram = new Hologram(outpostManager.getClansManager().getHologramManager(), _origin.clone().add(0.5, 2.3, 0.5), F.elem(_ownerClan.getName()) + C.cWhite + "'s Outpost block (Right-Click to activate)"); + _preHologram2 = new Hologram(outpostManager.getClansManager().getHologramManager(), _origin.clone().add(0.5, 3, 0.5), "Despawning: " + UtilText.getProgress(null, 0, null, true)); if (token.OutpostState == OutpostState.AWAITING) { @@ -175,6 +181,16 @@ public class Outpost implements Listener List reverse = circleAround(new Vector(0., 0., 0.), 40, .6d); Collections.reverse(reverse); _reverseCircleStages = new LoopIterator(reverse); + + if (token.OutpostState == OutpostState.CONSTRUCTING || token.OutpostState == OutpostState.LIVE) + { + _nameHolograms.add(new Hologram(outpostManager.getClansManager().getHologramManager(), _origin.clone().add((_type._size / 2) + 1, 2, 0), C.cGreen + _ownerClan.getName())); + _nameHolograms.add(new Hologram(outpostManager.getClansManager().getHologramManager(), _origin.clone().subtract((_type._size / 2) + 1, -2, 0), C.cGreen + _ownerClan.getName())); + _nameHolograms.add(new Hologram(outpostManager.getClansManager().getHologramManager(), _origin.clone().add(0, 2, (_type._size / 2) + 1), C.cGreen + _ownerClan.getName())); + _nameHolograms.add(new Hologram(outpostManager.getClansManager().getHologramManager(), _origin.clone().subtract(0, -2, (_type._size / 2) + 1), C.cGreen + _ownerClan.getName())); + + _nameHolograms.forEach(Hologram::start); + } } public Outpost(OutpostManager outpostManager, ClanInfo clan, Location location, OutpostType type) @@ -220,6 +236,11 @@ public class Outpost implements Listener _reverseCircleStages = new LoopIterator(reverse); UtilServer.registerEvents(this); + + _nameHolograms.add(new Hologram(outpostManager.getClansManager().getHologramManager(), _origin.clone().add((_type._size / 2) + 1, 2, 0), C.cGreen + _ownerClan.getName())); + _nameHolograms.add(new Hologram(outpostManager.getClansManager().getHologramManager(), _origin.clone().subtract((_type._size / 2) + 1, -2, 0), C.cGreen + _ownerClan.getName())); + _nameHolograms.add(new Hologram(outpostManager.getClansManager().getHologramManager(), _origin.clone().add(0, 2, (_type._size / 2) + 1), C.cGreen + _ownerClan.getName())); + _nameHolograms.add(new Hologram(outpostManager.getClansManager().getHologramManager(), _origin.clone().subtract(0, -2, (_type._size / 2) + 1), C.cGreen + _ownerClan.getName())); } private void cleanup() @@ -237,12 +258,47 @@ public class Outpost implements Listener _weapons.forEach(SiegeWeapon::kill); _weapons.clear(); + _nameHolograms.forEach(Hologram::stop); + _outpostManager.queueForRemoval(_ownerClan.getName()); } - @EventHandler + @EventHandler(priority = EventPriority.HIGH) public void onInteract(PlayerInteractEvent event) { + if (getState() == OutpostState.LIVE) + { + do + { + if (event.getClickedBlock() == null) + { + break; + } + + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) + { + break; + } + + if (!UtilItem.isDoor(event.getClickedBlock().getType())) + { + break; + } + + if (_ownerClan.isMember(event.getPlayer())) + { + break; + } + + if (UtilAlg.inBoundingBox(event.getClickedBlock().getLocation(), _startCorner.clone().subtract(.5, 0, .5), _endCorner)) + { + UtilPlayer.message(event.getPlayer(), F.main("Clans", "You cannot open the doors of this Outpost.")); + event.setCancelled(true); + return; + } + } while(false); + } + if (getState() != OutpostState.AWAITING) { return; @@ -274,7 +330,7 @@ public class Outpost implements Listener } } - @EventHandler(priority = EventPriority.HIGHEST) + @EventHandler public void fireBow(EntityShootBowEvent event) { if (!(event.getEntity() instanceof Player) || !(event.getProjectile() instanceof Arrow)) @@ -282,10 +338,7 @@ public class Outpost implements Listener return; } - event.setCancelled(true); - Player player = (Player) event.getEntity(); - Arrow arrow = player.shootArrow(); if (_ownerClan.isMember(player)) { @@ -300,6 +353,8 @@ public class Outpost implements Listener if (item.isSimilar(SIEGE_DECLARATION_ARROW)) { + Arrow arrow = (Arrow) event.getProjectile(); + if (_againstClan == null && !arrow.hasMetadata("OutpostData")) { arrow.setMetadata("OutpostData", new FixedMetadataValue(_outpostManager.getPlugin(), _ownerClan.getName() + ";" + player.getName())); @@ -357,7 +412,7 @@ public class Outpost implements Listener }; } - @EventHandler + @EventHandler(priority = EventPriority.HIGH) public void onBlockPlace(BlockPlaceEvent event) { if (event.isCancelled()) return; @@ -368,6 +423,16 @@ public class Outpost implements Listener event.setCancelled(true); } } + + @EventHandler(priority = EventPriority.HIGH) + public void claimTerritory(PlayerClaimTerritoryEvent event) + { + if (UtilMath.offset2d(event.getClaimedChunk().getBlock(8, 0, 8).getLocation(), _origin) < 32) + { + UtilPlayer.message(event.getClaimer(), F.main("Clans", "You may not claim this close to an Outpost.")); + event.setCancelled(true); + } + } protected void update() { @@ -387,7 +452,7 @@ public class Outpost implements Listener } else if (_againstClan != null) { - UtilTextBottom.display("Enemy Outpost Health: " + UtilText.colorPercentage(getHealthPercentage()) + "%", _againstClan.getOnlinePlayersArray()); + UtilTextBottom.display("Enemy Outpost Health: " + C.cYellow + getHealthPercentage() + "%", _againstClan.getOnlinePlayersArray()); } if (_state == OutpostState.AWAITING) @@ -428,12 +493,10 @@ public class Outpost implements Listener { if (block.getId() == Material.CHEST.getId()) { - for (int slot = 0; slot < 8; slot++) + for (int slot = 0; slot < ((Chest) block.getLocation().getBlock().getState()).getInventory().getSize(); slot++) { ((Chest) block.getLocation().getBlock().getState()).getInventory().setItem(slot, SIEGE_DECLARATION_ARROW); } - - break; } } @@ -537,6 +600,8 @@ public class Outpost implements Listener } } } + + _nameHolograms.forEach(Hologram::start); } private List circleAround(Vector origin, int points, double radius) @@ -560,7 +625,7 @@ public class Outpost implements Listener NonFinalInteger wait = new NonFinalInteger(0); - _blocks.values().stream().filter(block -> UtilMath.random.nextBoolean()).filter(block -> UtilMath.random.nextBoolean()).limit(20).forEach(block -> + _blocks.values().stream().filter(block -> UtilMath.random.nextBoolean() && UtilMath.random.nextBoolean()).filter(block -> UtilMath.random.nextBoolean()).limit(20).forEach(block -> _outpostManager.runSyncLater(() -> { UtilParticle.PlayParticle(ParticleType.HUGE_EXPLOSION, block.getLocation(), new Vector(0,0,0), 1f, 1, ViewDist.MAX); _origin.getWorld().playSound(block.getLocation(), Sound.EXPLODE, 1.0f, 1.0f); @@ -680,7 +745,7 @@ public class Outpost implements Listener { return _againstClan; } - + public void declareOn(Player declarer, ClanInfo against) { if (_againstClan != null) diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/outpost/OutpostManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/outpost/OutpostManager.java index e365b380d..45d57bfb0 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/outpost/OutpostManager.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/outpost/OutpostManager.java @@ -52,8 +52,6 @@ public class OutpostManager extends MiniPlugin private OutpostRepository _repository; - private Stack _addQueue = new Stack<>(); - public OutpostManager(ClansManager clansManager, SiegeManager siegeManager) { super("Outpost Manager", clansManager.getPlugin()); @@ -224,13 +222,6 @@ public class OutpostManager extends MiniPlugin @EventHandler public void update(UpdateEvent event) { - if (!_addQueue.isEmpty()) - { - Outpost outpost = _addQueue.pop(); - - _outposts.put(outpost.getOwner().getName(), outpost); - } - for (Outpost outpost : _outposts.values()) { if (outpost.getState() != OutpostState.DEAD) @@ -322,7 +313,7 @@ public class OutpostManager extends MiniPlugin if (outpost != null) { - if (event.getWeapon().getOwner().getName().equals(outpost.getAgainst().getName())) + if (outpost.getAgainst() != null && event.getWeapon().getOwner().getName().equals(outpost.getAgainst().getName())) { if (outpost.getHealth() > 0) { @@ -390,32 +381,38 @@ public class OutpostManager extends MiniPlugin return _clansManager; } - public void loadOutposts() + public void loadOutposts(Runnable post) { System.out.println("[OUTPOSTS] LOADING OUTPOSTS FROM DATABASE"); _repository.getOutpostsByServer(_clansManager.getServerId(), tokens -> { - for (OutpostToken token : tokens) - { + tokens.forEach(token -> { + Outpost outpost = new Outpost(this, token); + if ((System.currentTimeMillis() - token.TimeSpawned) > Outpost.MAX_LIFETIME) { System.out.println("[OUTPOSTS] SKIPPING & REMOVING OUTPOST [" + token.UniqueId + "] BECAUSE OF OLD AGE"); _repository.deleteOutpost(token.UniqueId); + outpost.kill(); - continue; + return; } - Outpost outpost = new Outpost(this, token); - System.out.println("[OUTPOSTS] INITIALIZED OUTPOST FROM DATABASE SAVE"); - _addQueue.push(outpost); + _outposts.put(token.OwnerClan.getName(), outpost); + }); + + if (post != null) + { + post.run(); } + }); } - public void saveOutposts() + public void saveOutposts(Runnable post) { final Stack queue = new Stack<>(); @@ -435,16 +432,17 @@ public class OutpostManager extends MiniPlugin } }); - runAsync(() -> - _repository.getOutpostsByServer(_clansManager.getServerId(), tokens -> + runAsync(() -> { + _repository.getOutpostsByServer(_clansManager.getServerId(), tokens -> { tokens.forEach(token -> { if (!_idToOutpost.containsKey(Integer.valueOf(token.UniqueId))) { + System.out.println("[OUTPOSTS] OUTPOST [" + token.UniqueId + "] NO LONGER EXISTS, DELETING"); _repository.deleteOutpost(token.UniqueId); } - }) - ) - ); + }); + }); + }); } public OutpostRepository getRepository() diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/repository/SiegeWeaponRepository.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/repository/SiegeWeaponRepository.java index 4a325efac..0027d92c7 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/repository/SiegeWeaponRepository.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/repository/SiegeWeaponRepository.java @@ -2,6 +2,7 @@ package mineplex.game.clans.clans.siege.repository; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Timestamp; import java.util.List; import org.bukkit.plugin.java.JavaPlugin; @@ -13,6 +14,8 @@ import mineplex.core.common.util.UtilWorld; import mineplex.core.database.DBPool; import mineplex.core.database.RepositoryBase; import mineplex.core.database.column.ColumnInt; +import mineplex.core.database.column.ColumnTimestamp; +import mineplex.core.database.column.ColumnVarChar; import mineplex.game.clans.clans.ClanInfo; import mineplex.game.clans.clans.siege.SiegeManager; import mineplex.game.clans.clans.siege.outpost.Outpost; @@ -28,13 +31,17 @@ public class SiegeWeaponRepository extends RepositoryBase + "ownerClan INT NOT NULL," + "outpostId INT," + "weaponType TINYINT NOT NULL," - + "health int NOT NULL," - + "yaw int NOT NULL," + + "health INT NOT NULL," + + "yaw INT NOT NULL," + "lastFired LONG);"; private static final String GET_WEAPON_BY_ID = "SELECT * FROM clansSiegeWeapons WHERE uniqueId=?;"; private static final String GET_WEAPONS_BY_CLAN = "SELECT * FROM clansSiegeWeapons WHERE ownerClan=?;"; private static final String GET_WEAPONS_BY_OUTPOST = "SELECT * FROM clansSiegeWeapons WHERE outpostId=?;"; + private static final String GET_WEAPONS_BY_SERVER = "SELECT * FROM clansSiegeWeapons WHERE serverId=?;"; + + private static final String UPDATE_WEAPON = "UPDATE clansSiegeWeapons SET health=?,yaw=?,lastFired=? WHERE uniqueId=?;"; + private static final String INSERT_WEAPON = "INSERT INTO clansSiegeWeapons VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);"; private static final String DELETE_WEAPON = "DELETE FROM clansSiegeWeapons WHERE uniqueId=?;"; @@ -62,6 +69,24 @@ public class SiegeWeaponRepository extends RepositoryBase callback.run(token); }, new ColumnInt("uniqueId", uniqueId)); } + + public void getWeaponsByServer(final int serverId, final Callback> callback) + { + executeQuery(GET_WEAPONS_BY_SERVER, resultSet -> { + List tokens = Lists.newArrayList(); + + while (resultSet.next()) + { + SiegeWeaponToken token = new SiegeWeaponToken(); + + load(token, resultSet); + + tokens.add(token); + } + + callback.run(tokens); + }, new ColumnInt("serverId", serverId)); + } public void getWeaponsByClan(final ClanInfo clan, final Callback> callback) { @@ -101,7 +126,7 @@ public class SiegeWeaponRepository extends RepositoryBase private void load(SiegeWeaponToken token, ResultSet columns) throws SQLException { - token.Id = columns.getInt("uniqueId"); + token.UniqueId = columns.getInt("uniqueId"); token.Location = UtilWorld.strToLoc(columns.getString("location")); token.OwnerClan = _manager.getClansManager().getClanUtility().getClanById(columns.getInt("ownerClan")); token.OutpostId = columns.getInt("outpostId"); @@ -110,6 +135,29 @@ public class SiegeWeaponRepository extends RepositoryBase token.Yaw = columns.getShort("yaw"); token.LastFired = columns.getTimestamp("lastFired").getTime(); } + + public void updateWeapon(SiegeWeaponToken token) + { + executeUpdate(UPDATE_WEAPON, + new ColumnInt("health", token.Health), + new ColumnInt("yaw", token.Yaw), + new ColumnTimestamp("lastFired", new Timestamp(token.LastFired)), + new ColumnInt("uniqueId", token.UniqueId)); + } + + public void insertWeapon(SiegeWeaponToken token) + { + executeUpdate(INSERT_WEAPON, + new ColumnInt("uniqueId", token.UniqueId), + new ColumnInt("serverId", _manager.getClansManager().getServerId()), + new ColumnVarChar("location", 30, UtilWorld.locToStr(token.Location)), + new ColumnInt("ownerClan", token.OwnerClan.getId()), + new ColumnInt("outpostId", token.OutpostId), + new ColumnInt("weaponType", token.WeaponType), + new ColumnInt("health", token.Health), + new ColumnInt("yaw", token.Yaw), + new ColumnTimestamp("lastFired", new Timestamp(token.LastFired))); + } @Override protected void initialize() diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/repository/tokens/SiegeWeaponToken.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/repository/tokens/SiegeWeaponToken.java index b07edf267..167c7ed2f 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/repository/tokens/SiegeWeaponToken.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/repository/tokens/SiegeWeaponToken.java @@ -1,22 +1,17 @@ package mineplex.game.clans.clans.siege.repository.tokens; -import java.util.Map; -import java.util.UUID; - import org.bukkit.Location; import mineplex.game.clans.clans.ClanInfo; public class SiegeWeaponToken { - public int Id; + public int UniqueId; public ClanInfo OwnerClan; public byte WeaponType; public int OutpostId; public Location Location; - public Map ComprisedOf; public int Health; public int Yaw; - public UUID Rider; public long LastFired; } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/Cannon.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/Cannon.java index e760c2d2a..5c7c38522 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/Cannon.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/Cannon.java @@ -1,20 +1,21 @@ package mineplex.game.clans.clans.siege.weapon; -import java.util.Map.Entry; -import java.util.UUID; - import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Slime; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; +import com.google.common.collect.Lists; + import mineplex.core.common.util.C; import mineplex.core.common.util.F; import mineplex.core.common.util.UtilEnt; @@ -179,37 +180,56 @@ public class Cannon extends SiegeWeapon private void loadEntities() { - Slime slime = _location.getWorld().spawn(_location, Slime.class); + Slime filler = _location.getWorld().spawn(_location, Slime.class); - UtilEnt.silence(slime, true); - UtilEnt.Vegetate(slime); + UtilEnt.silence(filler, true); + UtilEnt.Vegetate(filler); - slime.setSize(-1); - slime.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 99999999, 1, true, false)); + filler.setSize(-1); + filler.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 99999999, 1, true, false)); - addEntity(slime, "Filler_1"); + addEntity(filler, "Filler_1"); - slime = _location.getWorld().spawn(_location, Slime.class); + Slime playerMount = _location.getWorld().spawn(_location, Slime.class); - UtilEnt.silence(slime, true); - UtilEnt.Vegetate(slime); + UtilEnt.silence(playerMount, true); + UtilEnt.Vegetate(playerMount); - slime.setSize(-1); - slime.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 99999999, 1, true, false)); + playerMount.setSize(-1); + playerMount.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 99999999, 1, true, false)); - getEntity("Filler_1").setPassenger(slime); - addEntity(slime, "PLAYERMOUNT"); + getEntity("Filler_1").setPassenger(playerMount); + addEntity(playerMount, "PLAYERMOUNT"); - ArmorStand armorStand = _location.getWorld().spawn(_location.clone().add(.5, 0, .5), ArmorStand.class); + ArmorStand weapon = _location.getWorld().spawn(_location.clone().add(.5, 0, .5), ArmorStand.class); - UtilEnt.setFakeHead(armorStand, true); - armorStand.teleport(_location); - armorStand.setVisible(false); - armorStand.setGravity(false); + UtilEnt.setFakeHead(weapon, true); + weapon.teleport(_location); + weapon.setVisible(false); + weapon.setGravity(false); - armorStand.setPassenger(getEntity("Filler_1")); + weapon.setPassenger(getEntity("Filler_1")); - addEntity(armorStand, "WEAPON"); + addEntity(weapon, "WEAPON"); + + weapon.setMetadata("WeaponId", new FixedMetadataValue(_siegeManager.getPlugin(), Integer.valueOf(_uniqueId))); + } + + @Override + public void FindEntities() + { + Lists.newArrayList(_location.getChunk().getEntities()) + .stream() + .filter(entity -> entity.getType().equals(EntityType.ARMOR_STAND)) + .filter(entity -> entity.hasMetadata("WeaponId")) + .filter(entity -> entity.getMetadata("WeaponId").get(0).asInt() == _uniqueId) + .forEach(entity -> { + + addEntity(entity, "WEAPON"); + addEntity(entity.getPassenger(), "Filler_1"); + addEntity(entity.getPassenger().getPassenger(), "PLAYERMOUNT"); + + }); } @Override diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/Catapult.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/Catapult.java index d4e70a40e..8e2abebef 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/Catapult.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/Catapult.java @@ -4,13 +4,18 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Slime; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; +import com.google.common.collect.Lists; + import mineplex.core.common.util.C; import mineplex.core.common.util.F; import mineplex.core.common.util.UtilEnt; @@ -56,6 +61,8 @@ public class Catapult extends SiegeWeapon setProjectileAttributes(new ProjectileAttributes().setFallingBlock().setFallingBlockType(Material.TNT).setDoCrater().craterSize(3).craterChanceOfAir(1.3d)); + _invertRotation = true; + _baseDamage = 550; _rotSpeed = 60.0f; @@ -181,28 +188,56 @@ public class Catapult extends SiegeWeapon private void loadEntities() { - ArmorStand armorStand = _location.getWorld().spawn(_location.clone().add(.5, .1, .5), ArmorStand.class); - - UtilEnt.setFakeHead(armorStand, true); - armorStand.setVisible(false); - armorStand.setGravity(false); - - addEntity(armorStand, "WEAPON"); - Slime filler = _location.getWorld().spawn(_location, Slime.class); + UtilEnt.silence(filler, true); + UtilEnt.Vegetate(filler); + filler.setSize(-1); - filler.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 0)); + filler.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 99999999, 1, true, false)); addEntity(filler, "Filler_1"); - ArmorStand mount = _location.getWorld().spawn(_location.clone().add(.5, 0, .5), ArmorStand.class); + Slime playerMount = _location.getWorld().spawn(_location, Slime.class); - mount.setVisible(false); - mount.setGravity(false); - filler.setPassenger(mount); + UtilEnt.silence(playerMount, true); + UtilEnt.Vegetate(playerMount); - addEntity(mount, "PLAYERMOUNT"); + playerMount.setSize(-1); + playerMount.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 99999999, 1, true, false)); + + getEntity("Filler_1").setPassenger(playerMount); + addEntity(playerMount, "PLAYERMOUNT"); + + ArmorStand weapon = _location.getWorld().spawn(_location.clone().add(.5, 0, .5), ArmorStand.class); + + UtilEnt.setFakeHead(weapon, true); + weapon.teleport(_location); + weapon.setVisible(false); + weapon.setGravity(false); + + weapon.setPassenger(getEntity("Filler_1")); + + addEntity(weapon, "WEAPON"); + + weapon.setMetadata("WeaponId", new FixedMetadataValue(_siegeManager.getPlugin(), Integer.valueOf(_uniqueId))); + } + + @Override + public void FindEntities() + { + Lists.newArrayList(_location.getChunk().getEntities()) + .stream() + .filter(entity -> entity.getType().equals(EntityType.ARMOR_STAND)) + .filter(entity -> entity.hasMetadata("WeaponId")) + .filter(entity -> entity.getMetadata("WeaponId").get(0).asInt() == _uniqueId) + .forEach(entity -> { + + addEntity(entity, "WEAPON"); + addEntity(entity.getPassenger(), "Filler_1"); + addEntity(entity.getPassenger().getPassenger(), "PLAYERMOUNT"); + + }); } @Override diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/SiegeWeapon.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/SiegeWeapon.java index 5955b5287..981ef34b7 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/SiegeWeapon.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/SiegeWeapon.java @@ -2,11 +2,8 @@ package mineplex.game.clans.clans.siege.weapon; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.UUID; import org.apache.commons.lang.Validate; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; @@ -25,6 +22,7 @@ import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.player.PlayerArmorStandManipulateEvent; import org.bukkit.event.player.PlayerInteractAtEntityEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.util.EulerAngle; @@ -129,13 +127,13 @@ public abstract class SiegeWeapon implements Listener protected boolean _alive = true; - protected final int _weaponTypeIdentifier; + protected final byte _weaponTypeIdentifier; public SiegeWeapon(int maxHealth, String name, SiegeWeaponToken token, ClansManager clansManager, SiegeManager siegeManager) { _weaponTypeIdentifier = token.WeaponType; - _uniqueId = token.Id; + _uniqueId = token.UniqueId; _outpost = siegeManager.getOutpostManager().Get(token.OutpostId); _siegeManager = siegeManager; @@ -155,29 +153,18 @@ public abstract class SiegeWeapon implements Listener _clans = clansManager; - for (Entity entity : token.Location.getWorld().getEntities()) - { - for (Entry entry : token.ComprisedOf.entrySet()) - { - if (entity.getUniqueId().equals(entry.getValue())) - { - _comprisedOf.add(entity); - _entityMapping.put(entry.getKey(), entity); - } - } - } - _yaw = token.Yaw; - _rider = Bukkit.getPlayer(token.Rider); _lastFired = token.LastFired; _health = token.Health; + + FindEntities(); } public SiegeWeapon(int typeId, Location location, int maxHealth, String name, ClanInfo owner, ClansManager clansManager, SiegeManager siegeManager, Outpost outpost) { _uniqueId = siegeManager.randomId(); - _weaponTypeIdentifier = typeId; + _weaponTypeIdentifier = (byte) typeId; _outpost = outpost; _siegeManager = siegeManager; @@ -193,6 +180,8 @@ public abstract class SiegeWeapon implements Listener _infoHologram = new Hologram(ClansManager.getInstance().getHologramManager(), _location.clone().add(.5, 3, .5), _name + " Health", getDisplayHealth()); _infoHologram.start(); + _siegeManager.getRepository().insertWeapon(toToken()); + UtilServer.getPluginManager().registerEvents(this, clansManager.getPlugin()); _clans = clansManager; @@ -222,6 +211,8 @@ public abstract class SiegeWeapon implements Listener UtilTextMiddle.display(UtilText.possesive(_ownerClan.getName(), _name), C.cRed + "-" + health, 5, 15, 5); } + protected abstract void FindEntities(); + protected abstract double[] getProjectileVelocity(); protected abstract String getNextState(); @@ -265,17 +256,15 @@ public abstract class SiegeWeapon implements Listener checkInv(); } - if (getRider() != null && getRider().getVehicle() == null) - { - System.out.println("Resetting rider"); - _rider = null; - } + _rider = (Player) getEntity("PLAYERMOUNT").getPassenger(); if (!getNextState().equals(_currentState)) { setState(getNextState()); } + + if (_projectile != null) { if (_projectile.hasDied()) @@ -459,8 +448,6 @@ public abstract class SiegeWeapon implements Listener getEntity("PLAYERMOUNT").setPassenger(player); } - _rider = player; - OnMount(player); } @@ -508,7 +495,6 @@ public abstract class SiegeWeapon implements Listener private void dismount(Player player) { player.teleport(player.getLocation().add(0, 1, 0)); - _rider = null; } private void handleLeftClick(Player player) @@ -930,6 +916,15 @@ public abstract class SiegeWeapon implements Listener } } + @EventHandler + public void chunkUnload(ChunkUnloadEvent event) + { + if (_comprisedOf.stream().anyMatch(entity -> entity.getLocation().getChunk().equals(event.getChunk()))) + { + event.setCancelled(true); + } + } + @EventHandler public void update(UpdateEvent event) { @@ -952,5 +947,20 @@ public abstract class SiegeWeapon implements Listener { return _boundingBoxSize; } + + public SiegeWeaponToken toToken() + { + SiegeWeaponToken token = new SiegeWeaponToken(); + + token.UniqueId = _uniqueId; + token.OwnerClan = _ownerClan; + token.WeaponType = _weaponTypeIdentifier; + token.OutpostId = _outpost == null ? 0 : _outpost.getUniqueId(); + token.Location = _location; + token.Health = _health; + token.Yaw = (int) _yaw; + + return token; + } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/gameplay/Gameplay.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/gameplay/Gameplay.java index 0d6daad86..b30233eee 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/gameplay/Gameplay.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/gameplay/Gameplay.java @@ -14,7 +14,6 @@ import org.bukkit.Sound; import org.bukkit.block.Biome; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; import org.bukkit.entity.EntityType; import org.bukkit.entity.ItemFrame; import org.bukkit.entity.Player; @@ -66,6 +65,7 @@ import mineplex.core.updater.UpdateType; import mineplex.core.updater.event.UpdateEvent; import mineplex.game.clans.clans.ClanInfo; import mineplex.game.clans.clans.ClansManager; +import mineplex.game.clans.clans.siege.outpost.Outpost; import mineplex.game.clans.core.repository.ClanTerritory; import mineplex.minecraft.game.classcombat.Class.ClientClass; import mineplex.minecraft.game.classcombat.Class.IPvpClass.ClassType; @@ -154,7 +154,7 @@ public class Gameplay extends MiniPlugin } } - @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + @EventHandler public void onBowShoot(EntityShootBowEvent event) { if (event.isCancelled()) @@ -165,6 +165,22 @@ public class Gameplay extends MiniPlugin if (event.getEntity() instanceof Player) { Player player = (Player) event.getEntity(); + + for (int slot = 0; slot < player.getInventory().getSize(); slot++) + { + ItemStack item = player.getInventory().getItem(slot); + + if (item == null) + { + continue; + } + + if (item.isSimilar(Outpost.SIEGE_DECLARATION_ARROW)) + { + return; + } + } + ClientClass playerClass = _clansManager.getClassManager().Get(player); if (!playerClass.IsGameClass(ClassType.Assassin, ClassType.Ranger))