com.sk89q.craftbook.sponge.mechanics.HeadDrops.java Source code

Java tutorial

Introduction

Here is the source code for com.sk89q.craftbook.sponge.mechanics.HeadDrops.java

Source

/*
 * CraftBook Copyright (C) 2010-2018 sk89q <http://www.sk89q.com>
 * CraftBook Copyright (C) 2011-2018 me4502 <http://www.me4502.com>
 * CraftBook Copyright (C) Contributors
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
 * License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program. If not,
 * see <http://www.gnu.org/licenses/>.
 */
package com.sk89q.craftbook.sponge.mechanics;

import static com.sk89q.craftbook.core.util.documentation.DocumentationGenerator.createStringOfLength;
import static com.sk89q.craftbook.core.util.documentation.DocumentationGenerator.padToLength;

import com.flowpowered.math.vector.Vector3d;
import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken;
import com.google.inject.Inject;
import com.me4502.modularframework.module.Module;
import com.me4502.modularframework.module.guice.ModuleConfiguration;
import com.sk89q.craftbook.core.util.ConfigValue;
import com.sk89q.craftbook.core.util.CraftBookException;
import com.sk89q.craftbook.core.util.PermissionNode;
import com.sk89q.craftbook.core.util.documentation.DocumentationProvider;
import com.sk89q.craftbook.sponge.CraftBookPlugin;
import com.sk89q.craftbook.sponge.mechanics.types.SpongeMechanic;
import com.sk89q.craftbook.sponge.util.SpongePermissionNode;
import ninja.leaping.configurate.ConfigurationNode;
import org.apache.commons.lang3.text.WordUtils;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.block.BlockTypes;
import org.spongepowered.api.block.tileentity.Skull;
import org.spongepowered.api.data.Transaction;
import org.spongepowered.api.data.key.Keys;
import org.spongepowered.api.data.manipulator.mutable.RepresentedPlayerData;
import org.spongepowered.api.data.type.HandTypes;
import org.spongepowered.api.data.type.SkullType;
import org.spongepowered.api.data.type.SkullTypes;
import org.spongepowered.api.entity.Entity;
import org.spongepowered.api.entity.EntityType;
import org.spongepowered.api.entity.EntityTypes;
import org.spongepowered.api.entity.Item;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.block.ChangeBlockEvent;
import org.spongepowered.api.event.block.InteractBlockEvent;
import org.spongepowered.api.event.cause.entity.damage.source.EntityDamageSource;
import org.spongepowered.api.event.filter.cause.First;
import org.spongepowered.api.event.item.inventory.DropItemEvent;
import org.spongepowered.api.item.ItemTypes;
import org.spongepowered.api.item.enchantment.EnchantmentTypes;
import org.spongepowered.api.item.inventory.ItemStack;
import org.spongepowered.api.profile.GameProfile;
import org.spongepowered.api.service.permission.PermissionDescription;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.format.TextColors;
import org.spongepowered.api.world.Location;
import org.spongepowered.api.world.World;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Nullable;

@Module(id = "headdrops", name = "HeadDrops", onEnable = "onInitialize", onDisable = "onDisable")
public class HeadDrops extends SpongeMechanic implements DocumentationProvider {

    private static final Pattern HEAD_DROPS_TABLE_PATTERN = Pattern.compile("%CUSTOM_HEAD_TYPES%", Pattern.LITERAL);

    @Inject
    @ModuleConfiguration
    public ConfigurationNode config;

    private Map<EntityType, GameProfile> mobSkullMap = new HashMap<>();

    private ConfigValue<Boolean> playerKillsOnly = new ConfigValue<>("player-kills-only",
            "Only drop heads when mobs are killed by a player.", true);
    private ConfigValue<Double> dropRate = new ConfigValue<>("drop-rate", "Drop chance out of 1.", 0.05,
            TypeToken.of(Double.class));
    private ConfigValue<Double> lootingRateModifier = new ConfigValue<>("looting-rate-modifier",
            "Added chance for each looting level.", 0.05, TypeToken.of(Double.class));
    private ConfigValue<Boolean> showNameClick = new ConfigValue<>("show-name-on-click",
            "Show the name of the owner of a head on right click.", true);
    private ConfigValue<Boolean> playerHeads = new ConfigValue<>("player-heads",
            "Allow players to drop their heads on death.", true);
    private ConfigValue<Boolean> mobHeads = new ConfigValue<>("mob-heads",
            "Allow mobs to drop their heads on death.", true);
    private ConfigValue<List<String>> ignoredNames = new ConfigValue<>("ignored-names",
            "List of usernames to ignore when the head is touched.", Lists.newArrayList("cscorelib"),
            new TypeToken<List<String>>() {
            });

    private SpongePermissionNode killPermission = new SpongePermissionNode("craftbook.headdrops.kill",
            "Allow the player to get a HeadDrop from killing an entity.", PermissionDescription.ROLE_USER);

    @Override
    public void onInitialize() throws CraftBookException {
        super.onInitialize();

        killPermission.register();

        playerKillsOnly.load(config);
        dropRate.load(config);
        lootingRateModifier.load(config);
        showNameClick.load(config);
        playerHeads.load(config);
        mobHeads.load(config);
        ignoredNames.load(config);

        mobSkullMap.clear();

        // Official or Guaranteed Static - Vanilla
        mobSkullMap.put(EntityTypes.BAT,
                GameProfile.of(UUID.fromString("339d5256-1cd8-4b79-bbd9-59874d25ba8f"), "bozzobrain"));
        mobSkullMap.put(EntityTypes.BLAZE,
                GameProfile.of(UUID.fromString("4c38ed11-596a-4fd4-ab1d-26f386c1cbac"), "MHF_Blaze"));
        mobSkullMap.put(EntityTypes.CAVE_SPIDER,
                GameProfile.of(UUID.fromString("cab28771-f0cd-4fe7-b129-02c69eba79a5"), "MHF_CaveSpider"));
        mobSkullMap.put(EntityTypes.CHICKEN,
                GameProfile.of(UUID.fromString("92deafa9-4307-42d9-b003-88601598d6c0"), "MHF_Chicken"));
        mobSkullMap.put(EntityTypes.COW,
                GameProfile.of(UUID.fromString("f159b274-c22e-4340-b7c1-52abde147713"), "MHF_Cow"));
        mobSkullMap.put(EntityTypes.DONKEY,
                GameProfile.of(UUID.fromString("5464dd67-5a46-4239-862f-b27b4944442c"), "Donkey"));
        mobSkullMap.put(EntityTypes.ELDER_GUARDIAN,
                GameProfile.of(UUID.fromString("57ef77e6-d9ea-493e-a0c7-564a36d9d12a"), "ElderGuardian"));
        mobSkullMap.put(EntityTypes.ENDERMAN,
                GameProfile.of(UUID.fromString("40ffb372-12f6-4678-b3f2-2176bf56dd4b"), "MHF_Enderman"));
        mobSkullMap.put(EntityTypes.ENDERMITE,
                GameProfile.of(UUID.fromString("3df6a050-b93e-4d8b-8fa4-b5228a797b84"), "MHF_Endermite"));
        mobSkullMap.put(EntityTypes.ENDER_DRAGON,
                GameProfile.of(UUID.fromString("bd3802bb-be48-438c-bafb-cb9510e2aa2d"), "MHF_EnderDragon"));
        mobSkullMap.put(EntityTypes.EVOCATION_ILLAGER,
                GameProfile.of(UUID.fromString("8b7d6844-f679-4c85-ab4d-0f99301f1899"), "MHF_Evoker"));
        mobSkullMap.put(EntityTypes.GHAST,
                GameProfile.of(UUID.fromString("063085a6-797f-4785-be1a-21cd7580f752"), "MHF_Ghast"));
        mobSkullMap.put(EntityTypes.GUARDIAN,
                GameProfile.of(UUID.fromString("4005cac1-a16a-45aa-9e72-7fb514335717"), "MHF_Guardian"));
        mobSkullMap.put(EntityTypes.HORSE,
                GameProfile.of(UUID.fromString("1b90edcf-393d-4e93-a0d6-cf737dc80999"), "gavertoso"));
        mobSkullMap.put(EntityTypes.IRON_GOLEM,
                GameProfile.of(UUID.fromString("757f90b2-2344-4b8d-8dac-824232e2cece"), "MHF_Golem"));
        mobSkullMap.put(EntityTypes.MAGMA_CUBE,
                GameProfile.of(UUID.fromString("0972bdd1-4b86-49fb-9ecc-a353f8491a51"), "MHF_LavaSlime"));
        mobSkullMap.put(EntityTypes.MUSHROOM_COW,
                GameProfile.of(UUID.fromString("a46817d6-73c5-4f3f-b712-af6b3ff47b96"), "MHF_MushroomCow"));
        mobSkullMap.put(EntityTypes.OCELOT,
                GameProfile.of(UUID.fromString("1bee9df5-4f71-42a2-bf52-d97970d3fea3"), "MHF_Ocelot"));
        mobSkullMap.put(EntityTypes.PARROT,
                GameProfile.of(UUID.fromString("3d88c411-c7e1-40f9-b1f7-fbe4b7aef4a2"), "MHF_Parrot"));
        mobSkullMap.put(EntityTypes.PIG,
                GameProfile.of(UUID.fromString("8b57078b-f1bd-45df-83c4-d88d16768fbe"), "MHF_Pig"));
        mobSkullMap.put(EntityTypes.PIG_ZOMBIE,
                GameProfile.of(UUID.fromString("18a2bb50-334a-4084-9184-2c380251a24b"), "MHF_PigZombie"));
        mobSkullMap.put(EntityTypes.POLAR_BEAR,
                GameProfile.of(UUID.fromString("38029209-38b0-4311-97a9-60663fbf74b5"), "Polar_Bear"));
        mobSkullMap.put(EntityTypes.RABBIT,
                GameProfile.of(UUID.fromString("fbec11d4-80a7-4c1c-9de3-4136a16f1de0"), "MHF_Rabbit"));
        mobSkullMap.put(EntityTypes.SHEEP,
                GameProfile.of(UUID.fromString("dfaad551-4e7e-45a1-a6f7-c6fc5ec823ac"), "MHF_Sheep"));
        mobSkullMap.put(EntityTypes.SHULKER,
                GameProfile.of(UUID.fromString("160f7d8a-c6b0-4fc8-8925-9e9d6c9c57d5"), "MHF_Shulker"));
        mobSkullMap.put(EntityTypes.SILVERFISH,
                GameProfile.of(UUID.fromString("6a4c6f38-243f-4c40-8342-749fa1009351"), "MHF_Silverfish"));
        mobSkullMap.put(EntityTypes.SLIME,
                GameProfile.of(UUID.fromString("870aba93-40e8-48b3-89c5-32ece00d6630"), "MHF_Slime"));
        mobSkullMap.put(EntityTypes.SNOWMAN,
                GameProfile.of(UUID.fromString("217f9e5e-601f-4a3d-879b-e10d30e3e59b"), "MHF_SnowGolem"));
        mobSkullMap.put(EntityTypes.SPIDER,
                GameProfile.of(UUID.fromString("5ad55f34-41b6-4bd2-9c32-18983c635936"), "MHF_Spider"));
        mobSkullMap.put(EntityTypes.STRAY,
                GameProfile.of(UUID.fromString("ed33403b-be7f-4a38-915c-abea93ebc9bc"), "MHF_Stray"));
        mobSkullMap.put(EntityTypes.SQUID,
                GameProfile.of(UUID.fromString("72e64683-e313-4c36-a408-c66b64e94af5"), "MHF_Squid"));
        mobSkullMap.put(EntityTypes.WITCH,
                GameProfile.of(UUID.fromString("fef85c49-2fdf-47f8-9132-552046243223"), "MHF_Witch"));
        mobSkullMap.put(EntityTypes.WITHER,
                GameProfile.of(UUID.fromString("39af6844-6809-4d2f-8ba4-7e92d087be18"), "MHF_Wither"));
        mobSkullMap.put(EntityTypes.WOLF,
                GameProfile.of(UUID.fromString("8d2d1d6d-8034-4c89-bd86-809a31fd5193"), "MHF_Wolf"));
        mobSkullMap.put(EntityTypes.VEX,
                GameProfile.of(UUID.fromString("f5f20997-217f-4426-8ab9-c6db6cce023f"), "MHF_Vex"));
        mobSkullMap.put(EntityTypes.VILLAGER,
                GameProfile.of(UUID.fromString("bd482739-767c-45dc-a1f8-c33c40530952"), "MHF_Villager"));
        mobSkullMap.put(EntityTypes.VINDICATION_ILLAGER,
                GameProfile.of(UUID.fromString("1f32ef1f-9bf1-436e-98e3-cb48758fd268"), "Vindicator"));
    }

    private Optional<ItemStack> getStackForEntity(EntityType type, @Nullable GameProfile profile) {
        SkullType skullType = null;

        if (type == EntityTypes.PLAYER) {
            if (playerHeads.getValue()) {
                skullType = SkullTypes.PLAYER;
            }
        } else if (mobHeads.getValue()) {
            if (type == EntityTypes.ZOMBIE) {
                skullType = SkullTypes.ZOMBIE;
            } else if (type == EntityTypes.CREEPER) {
                skullType = SkullTypes.CREEPER;
            } else if (type == EntityTypes.SKELETON) {
                skullType = SkullTypes.SKELETON;
            } else if (type == EntityTypes.WITHER_SKELETON) {
                skullType = SkullTypes.WITHER_SKELETON;
            } else if (type == EntityTypes.ENDER_DRAGON) {
                skullType = SkullTypes.ENDER_DRAGON;
            } else {
                // Add extra mob.
                profile = getForEntity(type);
                if (profile != null) {
                    skullType = SkullTypes.PLAYER;
                }
            }
        }

        if (skullType != null) {
            ItemStack itemStack = Sponge.getGame().getRegistry().createBuilder(ItemStack.Builder.class)
                    .itemType(ItemTypes.SKULL).add(Keys.SKULL_TYPE, skullType).build();
            if (profile != null) {
                if (profile.getName().isPresent() && ignoredNames.getValue().contains(profile.getName().get())) {
                    return Optional.empty();
                }
                RepresentedPlayerData skinData = Sponge.getGame().getDataManager()
                        .getManipulatorBuilder(RepresentedPlayerData.class).get().create();
                skinData = skinData.set(Keys.REPRESENTED_PLAYER, profile);
                if (type == EntityTypes.PLAYER) {
                    itemStack.offer(Keys.DISPLAY_NAME,
                            Text.of(TextColors.RESET, WordUtils.capitalize(type.getName()) + "'s Head"));
                } else {
                    itemStack.offer(Keys.DISPLAY_NAME,
                            Text.of(TextColors.RESET, WordUtils.capitalize(type.getName()) + " Head"));
                }
                itemStack.offer(skinData);
                return Optional.of(itemStack);
            }
        }

        return Optional.empty();
    }

    @Listener
    public void onItemDrops(DropItemEvent.Destruct event, @First Entity spawnCause) {
        EntityDamageSource damageSource = event.getCause().first(EntityDamageSource.class).orElse(null);
        Entity killer = null;

        if (damageSource != null) {
            killer = damageSource.getSource();
            if (killer instanceof Subject && !killPermission.hasPermission((Subject) killer)) {
                return;
            }
        }

        if (playerKillsOnly.getValue() && !(killer instanceof Player)) {
            return;
        }

        double chance = Math.min(1, dropRate.getValue());

        if (killer != null && killer instanceof Player) {
            int level = ((Player) killer).getItemInHand(HandTypes.MAIN_HAND)
                    .filter(item -> item.get(Keys.ITEM_ENCHANTMENTS).isPresent())
                    .map(item -> item.get(Keys.ITEM_ENCHANTMENTS).get().stream()
                            .filter(enchant -> enchant.getType().equals(EnchantmentTypes.LOOTING)).findFirst())
                    .filter(Optional::isPresent).map(enchant -> enchant.get().getLevel()).orElse(0);
            chance = Math.min(1, chance + (lootingRateModifier.getValue() * level));
        }

        if (ThreadLocalRandom.current().nextDouble() > chance) {
            return;
        }

        GameProfile profile = null;
        if (spawnCause instanceof Player) {
            profile = ((Player) spawnCause).getProfile();
        }

        getStackForEntity(spawnCause.getType(), profile).ifPresent(itemStack -> {
            Vector3d location = spawnCause.getLocation().getPosition();
            Item item = (Item) spawnCause.getWorld().createEntity(EntityTypes.ITEM, location);
            item.offer(Keys.REPRESENTED_ITEM, itemStack.createSnapshot());
            spawnCause.getWorld().spawnEntity(item);
        });
    }

    @Listener
    public void onPlayerInteract(InteractBlockEvent.Secondary.MainHand event, @First Player player) {
        if (!showNameClick.getValue()) {
            return;
        }
        event.getTargetBlock().getLocation().filter(location -> location.getBlockType() == BlockTypes.SKULL)
                .ifPresent(location -> {
                    Skull skull = (Skull) location.getTileEntity().get();
                    if (skull.skullType().get() == SkullTypes.PLAYER) {
                        GameProfile profile = skull.get(Keys.REPRESENTED_PLAYER).orElse(null);
                        if (profile != null) {
                            if (mobSkullMap.containsValue(profile)) {
                                EntityType entityType = mobSkullMap.entrySet().stream()
                                        .filter(entry -> entry.getValue().equals(profile)).findFirst().get()
                                        .getKey();
                                profile.getName().ifPresent(name -> player.sendMessage(Text.of(TextColors.YELLOW,
                                        "The severed head of a " + entityType.getName())));
                            } else {
                                profile.getName().ifPresent(name -> player
                                        .sendMessage(Text.of(TextColors.YELLOW, "The severed head of " + name)));
                            }
                        }
                    }
                });
    }

    @Listener
    public void onBlockBreak(ChangeBlockEvent.Break event) {
        event.getTransactions().stream()
                .filter(transaction -> transaction.getOriginal().getState().getType() == BlockTypes.SKULL)
                .map(Transaction::getOriginal).forEach(snapshot -> {
                    Location<World> location = snapshot.getLocation().get();
                    if (snapshot.get(Keys.SKULL_TYPE).get() == SkullTypes.PLAYER) {
                        GameProfile profile = snapshot.get(Keys.REPRESENTED_PLAYER).orElse(null);
                        if (profile != null) {
                            EntityType entityType = null;

                            if (mobSkullMap.containsValue(profile)) {
                                entityType = mobSkullMap.entrySet().stream()
                                        .filter(entry -> entry.getValue().equals(profile)).findFirst().get()
                                        .getKey();
                            } else {
                                if (profile.getName().isPresent()) {
                                    entityType = EntityTypes.PLAYER;
                                }
                            }

                            if (entityType != null) {
                                getStackForEntity(entityType, profile).ifPresent(itemStack -> {
                                    Item item = (Item) location.getExtent().createEntity(EntityTypes.ITEM,
                                            location.getPosition().add(0.5, 0.5, 0.5));
                                    item.offer(Keys.REPRESENTED_ITEM, itemStack.createSnapshot());
                                    location.getExtent().spawnEntity(item);

                                    event.setCancelled(true);
                                    Sponge.getScheduler().createTaskBuilder()
                                            .execute(() -> location.setBlockType(BlockTypes.AIR))
                                            .submit(CraftBookPlugin.spongeInst().container);
                                });
                            }
                        }
                    }
                });
    }

    private GameProfile getForEntity(EntityType entityType) {
        return mobSkullMap.get(entityType);
    }

    @Override
    public String getPath() {
        return "mechanics/head_drops";
    }

    @Override
    public String performCustomConversions(String input) {
        StringBuilder headTable = new StringBuilder();

        headTable.append("Mob Head Drops\n");
        headTable.append("=================\n\n");

        headTable.append("HeadDrops supports all mob heads in the base game, as well as many more.\n\n");

        int mobTypeLength = "Mob".length(), headImageLength = "Image".length();

        for (Map.Entry<EntityType, GameProfile> entry : mobSkullMap.entrySet()) {
            if (entry.getKey().getName().length() > mobTypeLength)
                mobTypeLength = entry.getKey().getName().length();
            if ((".. image:: https://minotar.net/helm/" + entry.getValue().getName().orElse("") + "/64.png")
                    .length() > headImageLength)
                headImageLength = (".. image:: https://minotar.net/helm/" + entry.getValue().getName().orElse("")
                        + "/64.png").length();
        }

        String border = createStringOfLength(mobTypeLength, '=') + ' ' + createStringOfLength(headImageLength, '=');

        headTable.append(border).append('\n');
        headTable.append(padToLength("Mob", mobTypeLength + 1)).append(padToLength("Image", headImageLength + 1))
                .append('\n');
        headTable.append(border).append('\n');
        for (Map.Entry<EntityType, GameProfile> entry : mobSkullMap.entrySet()) {
            headTable.append(padToLength(entry.getKey().getName(), mobTypeLength + 1)).append(padToLength(
                    ".. image:: https://minotar.net/helm/" + entry.getValue().getName().orElse("") + "/64.png",
                    headImageLength + 1)).append('\n');
        }
        headTable.append(border).append('\n');

        return HEAD_DROPS_TABLE_PATTERN.matcher(input).replaceAll(Matcher.quoteReplacement(headTable.toString()));
    }

    @Override
    public ConfigValue<?>[] getConfigurationNodes() {
        return new ConfigValue<?>[] { playerKillsOnly, dropRate, lootingRateModifier, showNameClick, playerHeads,
                mobHeads, ignoredNames };
    }

    @Override
    public PermissionNode[] getPermissionNodes() {
        return new PermissionNode[] { killPermission };
    }
}