package tc.oc.pgm.kits; import org.bukkit.entity.Player; import tc.oc.pgm.features.FeatureDefinition; import tc.oc.pgm.features.FeatureInfo; import tc.oc.pgm.match.MatchPlayer; import tc.oc.pgm.match.MatchScope; @FeatureInfo(name = "kit") public interface Kit extends FeatureDefinition { /** * Apply this kit to the given player. If force is true, the player's state is made * to match the kit as strictly as possible, otherwise the kit may be given to the * player in a way that is more in their best interest. Subclasses will interpret * these concepts in their own way. * * A mutable List must be given, to which the Kit may add ItemStacks that could not * be applied normally, because the player's inventory was full. These stacks will * be given to the player using the natural give algorithm after ALL kits have been * applied. This phase must be deferred in this way so that overflow from one kit * does not displace stacks in another kit applied simultaneously. In this way, the * number of stacks that go to their proper slots is maximized. */ void apply(MatchPlayer player, boolean force, ItemKitApplicator items); default void remove(MatchPlayer player) { throw new UnsupportedOperationException(this + " is not removable"); } default boolean isRemovable() { return false; } default void apply(MatchPlayer player) { apply(player, false); } default void apply(MatchPlayer player, boolean force) { final ItemKitApplicator items = new ItemKitApplicator(); apply(player, force, items); items.apply(player); /** * When max health is lowered by an item attribute or potion effect, the client can * go into an inconsistent state that has strange effects, like the death animation * playing when the player isn't dead. It is probably related to this bug: * * https://bugs.mojang.com/browse/MC-19690 * * This appears to fix the client state, for reasons that are unclear. The one tick * delay is necessary. Any less and getMaxHealth will not reflect whatever was * applied in the kit to modify it. */ final Player bukkit = player.getBukkit(); player.getMatch().getScheduler(MatchScope.LOADED).createDelayedTask(1, () -> { if(bukkit.isOnline() && !player.isDead() && bukkit.getMaxHealth() < 20) { bukkit.setHealth(Math.min(bukkit.getHealth(), bukkit.getMaxHealth())); } }); } abstract class Impl extends FeatureDefinition.Impl implements Kit {} }