diff --git a/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties b/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties index cb00de6b9..1598ee6ad 100644 --- a/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties +++ b/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties @@ -255,6 +255,10 @@ mutation.type.tools = Tools mutation.type.tools.desc = enchanted diamond tools mutation.type.apocalypse = Apocalypse mutation.type.apocalypse.desc = mob spawning chaos +mutation.type.teamchest = Team Chest +mutation.type.teamchest.desc = access a shared team chest +mutation.type.teamchest.item_name = Team Chest +mutation.type.teamchest.item_lore = Right click to access an inventory shared by the team. tnt.license.info.alreadyHas = You have a TNT license. You can surrender your license by typing {0} tnt.license.info.doesNotHave = You do not have a TNT license. You can request one by typing {0} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/Mutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/Mutation.java index 75e2a9c98..bef30b094 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/Mutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/Mutation.java @@ -42,7 +42,8 @@ public enum Mutation { BREAD (BreadMutation.class, Material.BREAD), BOAT (BoatMutation.class, Material.BOAT, false), TOOLS (ToolsMutation.class, Material.DIAMOND_PICKAXE), - APOCALYPSE (ApocalypseMutation.class, Material.NETHER_STAR); + APOCALYPSE (ApocalypseMutation.class, Material.NETHER_STAR), + TEAMCHEST (TeamChestMutation.class, Material.ENDER_CHEST); public static final String TYPE_KEY = "mutation.type."; public static final String DESCRIPTION_KEY = ".desc"; diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/TeamChestMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/TeamChestMutation.java new file mode 100644 index 000000000..b6e398c18 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/TeamChestMutation.java @@ -0,0 +1,140 @@ +package tc.oc.pgm.mutation.types.kit; + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import tc.oc.commons.bukkit.inventory.Slot; +import tc.oc.commons.bukkit.item.ItemBuilder; +import tc.oc.pgm.PGMTranslations; +import tc.oc.pgm.kits.ItemKit; +import tc.oc.pgm.kits.Kit; +import tc.oc.pgm.kits.SlotItemKit; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.match.Party; +import tc.oc.pgm.mutation.types.KitMutation; +import tc.oc.pgm.wool.WoolMatchModule; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.WeakHashMap; + +public class TeamChestMutation extends KitMutation { + final static Material TOOL_TYPE = Material.ENDER_CHEST; + final static int CHEST_SIZE = 27; + + final Map teamChests = new WeakHashMap<>(); + + final Optional optWools; + + public TeamChestMutation(Match match) { + super(match, false); + optWools = match().module(WoolMatchModule.class); + } + + @Override + public void enable() { + super.enable(); + for (Party party : match().getParties()) { + if (party.isParticipatingType()) { + // TODO: Could the chest title be localized properly? + teamChests.put(party, match().getServer().createInventory(null, CHEST_SIZE)); + } + } + } + + @Override + public void disable() { + teamChests.clear(); + super.disable(); + } + + @Override + public void kits(MatchPlayer player, List kits) { + super.kits(player, kits); + kits.add(getKitForPlayer(player)); + } + + // Open shared inventory instead of placing the chest + @EventHandler(priority = EventPriority.HIGHEST) + public void onChestUse(PlayerInteractEvent event) { + Player bukkitPlayer = event.getPlayer(); + Optional optPlayer = match().participant((Entity) bukkitPlayer); + if (!optPlayer.isPresent() || + !(event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) || + event.getItem() == null || + event.getItem().getType() != TOOL_TYPE) { + return; + } + + Optional optTeamInventory = getTeamsInventory(bukkitPlayer); + optTeamInventory.ifPresent(teamInventory -> { + event.setCancelled(true); + // If the item is in the off-hand slot, it wont get put back visually for the player without this. + if(event.getHand() == EquipmentSlot.OFF_HAND) event.getActor().updateInventory(); + bukkitPlayer.openInventory(teamInventory); + }); + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onInventoryClick(InventoryClickEvent event) { + Player player = event.getActor(); + if (event.getCurrentItem() == null) return; + + // No putting blacklisted items (ender chest, possibly wool) into the chest + Optional teamChest = getTeamsInventory(player); + if (teamChest.map(teamInventory -> teamInventory.equals(event.getView().getTopInventory())).orElse(false) && + isBlacklistedItem(event.getCurrentItem())) { + event.setCancelled(true); + return; + } + + // If normal right click, in their inventory, on the chest, then open shared inventory. + getTeamsInventory(player).ifPresent(teamInventory -> { + if (event.getInventory().getType() == InventoryType.CRAFTING && + event.getCurrentItem().getType() == TOOL_TYPE && + event.getAction() == InventoryAction.PICKUP_HALF) { + event.setCancelled(true); + // This resets their mouse position annoyingly, but without it items can get stuck in places. + player.closeInventory(); + // Prevent visual inconsistencies, specifically off-hand slot + if (event.getSlotType() == InventoryType.SlotType.QUICKBAR) { + player.updateInventory(); + } + player.openInventory(teamInventory); + } + }); + } + + private boolean isBlacklistedItem(ItemStack item) { + return item.getType() == TOOL_TYPE || + optWools.map(w -> w.isObjectiveWool(item)).orElse(false); + } + + private Optional getTeamsInventory(Player bukkitPlayer) { + return match().participant((Entity) bukkitPlayer) + .map(matchPlayer -> teamChests.get(matchPlayer.getParty())); + } + + private Kit getKitForPlayer(MatchPlayer player) { + ItemStack stack = new ItemBuilder(item(TOOL_TYPE)) + .name(ChatColor.DARK_PURPLE + PGMTranslations.t("mutation.type.teamchest.item_name", player)) + .lore(ChatColor.DARK_AQUA + PGMTranslations.t("mutation.type.teamchest.item_lore", player)) + .get(); + + ItemKit kit = new SlotItemKit(stack, Slot.Player.forIndex(17)); + return kit; + } +} diff --git a/PGM/src/main/java/tc/oc/pgm/wool/WoolMatchModule.java b/PGM/src/main/java/tc/oc/pgm/wool/WoolMatchModule.java index e55ae9dc9..de2ad2e71 100644 --- a/PGM/src/main/java/tc/oc/pgm/wool/WoolMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/wool/WoolMatchModule.java @@ -63,7 +63,7 @@ public class WoolMatchModule extends MatchModule implements Listener { .collect(Collectors.toImmutableList()); } - private boolean isObjectiveWool(ItemStack stack) { + public boolean isObjectiveWool(ItemStack stack) { if(stack.getType() == Material.WOOL) { for(MonumentWool wool : this.wools) { if(wool.getDefinition().isObjectiveWool(stack)) return true;