ProjectAres/PGM/src/main/java/tc/oc/pgm/wool/WoolMatchModule.java

225 lines
9.5 KiB
Java

package tc.oc.pgm.wool;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.inject.Inject;
import com.google.common.collect.ImmutableList;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import org.bukkit.ChatColor;
import org.bukkit.DyeColor;
import org.bukkit.Material;
import org.bukkit.block.BlockState;
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.BlockPlaceEvent;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.inventory.PrepareItemCraftEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import tc.oc.commons.bukkit.util.BlockUtils;
import tc.oc.commons.bukkit.util.BukkitUtils;
import tc.oc.commons.core.stream.Collectors;
import tc.oc.time.Time;
import tc.oc.pgm.Config;
import tc.oc.pgm.PGMTranslations;
import tc.oc.pgm.events.BlockTransformEvent;
import tc.oc.pgm.events.ListenerScope;
import tc.oc.pgm.events.ParticipantBlockTransformEvent;
import tc.oc.pgm.goals.Contribution;
import tc.oc.pgm.goals.events.GoalCompleteEvent;
import tc.oc.pgm.goals.events.GoalStatusChangeEvent;
import tc.oc.pgm.match.Match;
import tc.oc.pgm.match.MatchModule;
import tc.oc.pgm.match.MatchPlayer;
import tc.oc.pgm.match.MatchScope;
import tc.oc.pgm.match.ParticipantState;
import tc.oc.pgm.match.Repeatable;
@ListenerScope(MatchScope.RUNNING)
public class WoolMatchModule extends MatchModule implements Listener {
private final List<MonumentWool> wools;
// Map of containers to a flag indicating whether they contained objective wool when the match started.
// For this to work, containers have to be checked for wool before their contents can be changed. To ensure this,
// containers are registered in this map the first time they are opened or accessed by a hopper or dispenser.
protected final Map<Inventory, Boolean> chests = new HashMap<>();
// Containers that did contain wool when the match started have an entry in this map representing the exact
// layout of the wools in the inventory. This is used to refill the container with wools.
protected final Map<Inventory, Map<Integer, ItemStack>> woolChests = new HashMap<>();
@Inject private WoolMatchModule(Match match, List<MonumentWoolFactory> wools) {
this.wools = wools.stream()
.map(def -> def.getGoal(match))
.collect(Collectors.toImmutableList());
}
private boolean isObjectiveWool(ItemStack stack) {
if(stack.getType() == Material.WOOL) {
for(MonumentWool wool : this.wools) {
if(wool.getDefinition().isObjectiveWool(stack)) return true;
}
}
return false;
}
private boolean containsObjectiveWool(Inventory inventory) {
for(MonumentWool wool : this.wools) {
if(wool.getDefinition().isHolding(inventory)) return true;
}
return false;
}
private void registerContainer(Inventory inv) {
// When a chest (or other block inventory) is accessed, check if it's a wool chest
Boolean isWoolChest = this.chests.get(inv);
if(isWoolChest == null) {
// If we haven't seen this chest yet, check it for wool
isWoolChest = this.containsObjectiveWool(inv);
this.chests.put(inv, isWoolChest);
if(isWoolChest) {
// If it is a wool chest, take a snapshot of the wools
Map<Integer, ItemStack> contents = new HashMap<>();
this.woolChests.put(inv, contents);
for(int slot = 0; slot < inv.getSize(); ++slot) {
ItemStack stack = inv.getItem(slot);
if(stack != null && this.isObjectiveWool(stack)) {
contents.put(slot, stack.clone());
}
}
}
}
}
@Repeatable(interval = @Time(seconds = 30), scope = MatchScope.RUNNING)
public void refillOneWoolPerContainer() {
if(!Config.Wool.autoRefillWoolChests()) return;
for(Entry<Inventory, Map<Integer, ItemStack>> chest : this.woolChests.entrySet()) {
Inventory inv = chest.getKey();
for(Entry<Integer, ItemStack> slotEntry : chest.getValue().entrySet()) {
int slot = slotEntry.getKey();
ItemStack wool = slotEntry.getValue();
ItemStack stack = inv.getItem(slotEntry.getKey());
if(stack == null) {
stack = wool.clone();
stack.setAmount(1);
inv.setItem(slot, stack);
break;
} else if(stack.isSimilar(wool) && stack.getAmount() < wool.getAmount()) {
stack.setAmount(stack.getAmount() + 1);
inv.setItem(slot, stack);
break;
}
}
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onInventoryOpen(InventoryOpenEvent event) {
// Register container blocks when they are opened
this.registerContainer(event.getInventory());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onItemTransfer(InventoryMoveItemEvent event) {
// When a hopper or dispenser transfers an item, register both blocks involved
this.registerContainer(event.getSource());
this.registerContainer(event.getDestination());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onContainerPlace(BlockPlaceEvent event) {
// Blacklist any placed container blocks
if(event.getBlock().getState() instanceof InventoryHolder) {
this.chests.put(((InventoryHolder) event.getBlock().getState()).getInventory(), false);
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void placementCheck(final BlockTransformEvent event) {
if(this.match.getWorld() != event.getWorld()) return;
final MonumentWool wool = this.findMonumentWool(BlockUtils.center(event.getNewState()).toVector());
if(wool == null) return;
if(event.getNewState().getType() == Material.AIR) { // block is being destroyed
if(isValidWool(wool.getDyeColor(), event.getOldState())) {
event.setCancelled(true);
}
return;
}
// default to cancelled; only uncancel if player is placing the correct color wool (see below)
event.setCancelled(true);
ParticipantState player = ParticipantBlockTransformEvent.getPlayerState(event);
if(player != null) { // wool can only be placed by a player
BaseComponent woolName = BukkitUtils.woolName(wool.getDyeColor());
if(!isValidWool(wool.getDyeColor(), event.getNewState())) {
player.sendWarning(new TranslatableComponent("match.wool.placeWrong", woolName), true);
} else if(wool.getOwner() != player.getParty()) {
player.sendWarning(new TranslatableComponent("match.wool.placeOther", wool.getOwner().getComponentName(), woolName), true);
} else {
event.setCancelled(false);
wool.markPlaced();
this.match.callEvent(new GoalStatusChangeEvent(wool));
this.match.callEvent(new PlayerWoolPlaceEvent(player, wool, event.getNewState()));
this.match.callEvent(new GoalCompleteEvent(wool,
true,
c -> false,
c -> c.equals(wool.getOwner()),
ImmutableList.of(new Contribution(player, 1))));
}
}
}
@EventHandler
public void handleWoolCrafting(PrepareItemCraftEvent event) {
if(event.getRecipe() == null) return;
ItemStack result = event.getRecipe().getResult();
InventoryHolder holder = event.getInventory().getHolder();
if (holder instanceof Player) {
MatchPlayer playerHolder = this.match.getPlayer((Player) holder);
if (playerHolder != null && result != null && result.getType() == Material.WOOL) {
for(MonumentWool wool : this.wools) {
if(wool.getDefinition().isObjectiveWool(result)) {
if(!wool.getDefinition().isCraftable()) {
playerHolder.sendMessage(ChatColor.RED + PGMTranslations.t("match.wool.craftDisabled", playerHolder, BukkitUtils.woolMessage(wool.getDyeColor())));
event.getInventory().setResult(null);
}
}
}
}
}
}
private MonumentWool findMonumentWool(Vector point) {
for(MonumentWool wool : this.wools) {
if(wool.getDefinition().getPlacementRegion().contains(point)) {
return wool;
}
}
return null;
}
@SuppressWarnings("deprecation")
private static boolean isValidWool(DyeColor expectedColor, BlockState state) {
return state.getType() == Material.WOOL && expectedColor.getWoolData() == state.getRawData();
}
}