All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.nukkit.entity.EntityIntelligentHuman Maven / Gradle / Ivy

There is a newer version: 1.20.40-r1
Show newest version
package cn.nukkit.entity;

import cn.nukkit.Player;
import cn.nukkit.api.PowerNukkitOnly;
import cn.nukkit.api.PowerNukkitXOnly;
import cn.nukkit.api.Since;
import cn.nukkit.block.BlockID;
import cn.nukkit.entity.data.Skin;
import cn.nukkit.event.entity.EntityDamageByEntityEvent;
import cn.nukkit.event.entity.EntityDamageEvent;
import cn.nukkit.inventory.*;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemShield;
import cn.nukkit.item.enchantment.Enchantment;
import cn.nukkit.level.Sound;
import cn.nukkit.level.format.FullChunk;
import cn.nukkit.level.vibration.VibrationEvent;
import cn.nukkit.level.vibration.VibrationType;
import cn.nukkit.math.NukkitMath;
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.network.protocol.AddPlayerPacket;
import cn.nukkit.network.protocol.RemoveEntityPacket;
import cn.nukkit.network.protocol.SetEntityLinkPacket;
import cn.nukkit.utils.*;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 用来提供给插件基础,以方便的使用带有智能的EntityHuman
 */
@PowerNukkitXOnly
@Since("1.19.50-r3")
public class EntityIntelligentHuman extends EntityIntelligent implements EntityInventoryHolder, IHuman {
    protected UUID uuid;
    protected byte[] rawUUID;
    protected Skin skin;
    protected FakeHumanInventory inventory;
    protected FakeHumanEnderChestInventory enderChestInventory;
    protected FakeHumanOffhandInventory offhandInventory;

    public EntityIntelligentHuman(FullChunk chunk, CompoundTag nbt) {
        super(chunk, nbt);
    }

    @Since("1.5.1.0-PN")
    @PowerNukkitOnly
    @Override
    public float getSwimmingHeight() {
        return getWidth();
    }

    @Override
    public float getWidth() {
        return 0.6f;
    }

    @Override
    public float getLength() {
        return 0.6f;
    }

    @Override
    public float getHeight() {
        return 1.8f;
    }

    @Override
    public float getEyeHeight() {
        return (float) (boundingBox.getMaxY() - boundingBox.getMinY() - 0.18);
    }

    @Override
    protected float getBaseOffset() {
        return 1.62f;
    }

    @Override
    public int getNetworkId() {
        return -1;
    }

    public Skin getSkin() {
        return skin;
    }

    public void setSkin(Skin skin) {
        this.skin = skin;
    }

    @Override
    public UUID getUniqueId() {
        return uuid;
    }

    @Override
    public void setUniqueId(UUID uuid) {
        this.uuid = uuid;
    }

    public byte[] getRawUniqueId() {
        return rawUUID;
    }

    @Override
    public FakeHumanInventory getInventory() {
        return inventory;
    }

    public FakeHumanEnderChestInventory getEnderChestInventory() {
        return enderChestInventory;
    }

    public FakeHumanOffhandInventory getOffhandInventory() {
        return offhandInventory;
    }

    @Override
    public void setInventories(Inventory[] inventory) {
        this.inventory = (FakeHumanInventory) inventory[0];
        this.offhandInventory = (FakeHumanOffhandInventory) inventory[1];
        this.enderChestInventory = (FakeHumanEnderChestInventory) inventory[2];
    }

    @Override
    protected void initEntity() {
        initHumanEntity(this);
        super.initEntity();
    }

    @Override
    public void saveNBT() {
        super.saveNBT();
        saveHumanEntity(this);
    }

    @Override
    public void updateMovement() {
        // 检测自由落体时间
        if (!this.onGround && this.y < this.highestPosition) {
            this.fallingTick++;
        }
        //这样做是为了向后兼容旧插件
        if (!enableHeadYaw()) {
            this.headYaw = this.yaw;
        }
        double diffPosition = (this.x - this.lastX) * (this.x - this.lastX) + (this.y - this.lastY) * (this.y - this.lastY) + (this.z - this.lastZ) * (this.z - this.lastZ);
        double diffRotation = enableHeadYaw() ? (this.headYaw - this.lastHeadYaw) * (this.headYaw - this.lastHeadYaw) : 0 + (this.yaw - this.lastYaw) * (this.yaw - this.lastYaw) + (this.pitch - this.lastPitch) * (this.pitch - this.lastPitch);
        double diffMotion = (this.motionX - this.lastMotionX) * (this.motionX - this.lastMotionX) + (this.motionY - this.lastMotionY) * (this.motionY - this.lastMotionY) + (this.motionZ - this.lastMotionZ) * (this.motionZ - this.lastMotionZ);
        if (diffPosition > 0.0001 || diffRotation > 1.0) { //0.2 ** 2, 1.5 ** 2
            if (diffPosition > 0.0001) {
                if (this.isOnGround()) {
                    this.level.getVibrationManager().callVibrationEvent(new VibrationEvent(this, this.clone(), VibrationType.STEP));
                } else if (this.isTouchingWater()) {
                    this.level.getVibrationManager().callVibrationEvent(new VibrationEvent(this, this.clone(), VibrationType.SWIM));
                }
            }
            this.broadcastMovement();
            this.lastX = this.x;
            this.lastY = this.y;
            this.lastZ = this.z;
            this.lastPitch = this.pitch;
            this.lastYaw = this.yaw;
            this.lastHeadYaw = this.headYaw;
            this.positionChanged = true;
        } else {
            this.positionChanged = false;
        }
        if (diffMotion > 0.0025 || (diffMotion > 0.0001 && this.getMotion().lengthSquared() <= 0.0001)) { //0.05 ** 2
            this.lastMotionX = this.motionX;
            this.lastMotionY = this.motionY;
            this.lastMotionZ = this.motionZ;
            this.addMotion(this.motionX, this.motionY, this.motionZ);
        }
        this.move(this.motionX, this.motionY, this.motionZ);
    }

    @Override
    public Item[] getDrops() {
        if (this.inventory != null) {
            List drops = new ArrayList<>(this.inventory.getContents().values());
            drops.addAll(this.offhandInventory.getContents().values());
            return drops.stream().filter(item -> !item.keepOnDeath()).toList().toArray(Item.EMPTY_ARRAY);
        }
        return Item.EMPTY_ARRAY;
    }

    @Override
    public boolean attack(EntityDamageEvent source) {
        if (this.isClosed() || !this.isAlive()) {
            return false;
        }

        if (source.getCause() != EntityDamageEvent.DamageCause.VOID && source.getCause() != EntityDamageEvent.DamageCause.CUSTOM && source.getCause() != EntityDamageEvent.DamageCause.MAGIC && source.getCause() != EntityDamageEvent.DamageCause.HUNGER) {
            int armorPoints = 0;
            int epf = 0;
            //int toughness = 0;

            for (Item armor : inventory.getArmorContents()) {
                armorPoints += armor.getArmorPoints();
                epf += calculateEnchantmentProtectionFactor(armor, source);
                //toughness += armor.getToughness();
            }

            if (source.canBeReducedByArmor()) {
                source.setDamage(-source.getFinalDamage() * armorPoints * 0.04f, EntityDamageEvent.DamageModifier.ARMOR);
            }

            source.setDamage(-source.getFinalDamage() * Math.min(NukkitMath.ceilFloat(Math.min(epf, 25) * ((float) ThreadLocalRandom.current().nextInt(50, 100) / 100)), 20) * 0.04f,
                    EntityDamageEvent.DamageModifier.ARMOR_ENCHANTMENTS);

            source.setDamage(-Math.min(this.getAbsorption(), source.getFinalDamage()), EntityDamageEvent.DamageModifier.ABSORPTION);
        }

        if (super.attack(source)) {
            Entity damager = null;

            if (source instanceof EntityDamageByEntityEvent) {
                damager = ((EntityDamageByEntityEvent) source).getDamager();
            }
            for (int slot = 0; slot < 4; slot++) {
                Item armorOld = this.inventory.getArmorItem(slot);
                if (armorOld.isArmor()) {
                    Item armor = damageArmor(armorOld, damager, source);
                    inventory.setArmorItem(slot, armor, armor.getId() != BlockID.AIR);
                }
            }
            return true;
        } else {
            return false;
        }
    }

    protected double calculateEnchantmentProtectionFactor(Item item, EntityDamageEvent source) {
        if (!item.hasEnchantments()) {
            return 0;
        }
        double epf = 0;
        if (item.applyEnchantments()) {
            for (Enchantment ench : item.getEnchantments()) {
                epf += ench.getProtectionFactor(source);
            }
        }
        return epf;
    }

    @Override
    public void setOnFire(int seconds) {
        int level = 0;
        for (Item armor : this.inventory.getArmorContents()) {
            Enchantment fireProtection = armor.getEnchantment(Enchantment.ID_PROTECTION_FIRE);

            if (fireProtection != null && fireProtection.getLevel() > 0) {
                level = Math.max(level, fireProtection.getLevel());
            }
        }
        seconds = (int) (seconds * (1 - level * 0.15));
        super.setOnFire(seconds);
    }

    @Override
    protected boolean applyNameTag(@NotNull Player player, @NotNull Item item) {
        return false;
    }

    protected Item damageArmor(Item armor, Entity damager, EntityDamageEvent event) {
        if (armor.hasEnchantments()) {
            if (damager != null) {
                if (armor.applyEnchantments()) {
                    for (Enchantment enchantment : armor.getEnchantments()) {
                        enchantment.doPostAttack(damager, this);
                    }
                }
            }

            Enchantment durability = armor.getEnchantment(Enchantment.ID_DURABILITY);
            if (durability != null
                    && durability.getLevel() > 0
                    && (100 / (durability.getLevel() + 1)) <= Utils.random.nextInt(100)) {
                return armor;
            }
        }

        if (event.getCause() != EntityDamageEvent.DamageCause.VOID &&
                event.getCause() != EntityDamageEvent.DamageCause.MAGIC &&
                event.getCause() != EntityDamageEvent.DamageCause.HUNGER &&
                event.getCause() != EntityDamageEvent.DamageCause.DROWNING &&
                event.getCause() != EntityDamageEvent.DamageCause.SUFFOCATION &&
                event.getCause() != EntityDamageEvent.DamageCause.SUICIDE &&
                event.getCause() != EntityDamageEvent.DamageCause.FIRE_TICK &&
                event.getCause() != EntityDamageEvent.DamageCause.FALL) { // No armor damage

            if (armor.isUnbreakable() || armor.getMaxDurability() < 0) {
                return armor;
            }

            if (armor instanceof ItemShield)
                armor.setDamage(armor.getDamage() + (event.getDamage() >= 3 ? (int) event.getDamage() + 1 : 0));
            else
                armor.setDamage(armor.getDamage() + Math.max(1, (int) (event.getDamage() / 4.0f)));

            if (armor.getDamage() >= armor.getMaxDurability()) {
                getLevel().addSound(this, Sound.RANDOM_BREAK);
                return Item.get(BlockID.AIR, 0, 0);
            }
        }

        return armor;
    }

    @Override
    public String getOriginalName() {
        return "EntityIntelligentHuman";
    }

    @Override
    public String getName() {
        return this.getNameTag();
    }

    @Override
    public void addMovement(double x, double y, double z, double yaw, double pitch, double headYaw) {
        this.level.addPlayerMovement(this, x, y, z, yaw, pitch, headYaw);
    }

    @Override
    public void spawnTo(Player player) {
        if (!this.hasSpawned.containsKey(player.getLoaderId())) {
            this.hasSpawned.put(player.getLoaderId(), player);

            if (!this.skin.isValid()) {
                throw new IllegalStateException(this.getClass().getSimpleName() + " must have a valid skin set");
            }

            this.server.updatePlayerListData(this.getUniqueId(), this.getId(), this.getName(), this.skin, new Player[]{player});

            AddPlayerPacket pk = new AddPlayerPacket();
            pk.uuid = this.getUniqueId();
            pk.username = this.getName();
            pk.entityUniqueId = this.getId();
            pk.entityRuntimeId = this.getId();
            pk.x = (float) this.x;
            pk.y = (float) this.y;
            pk.z = (float) this.z;
            pk.speedX = (float) this.motionX;
            pk.speedY = (float) this.motionY;
            pk.speedZ = (float) this.motionZ;
            pk.yaw = (float) this.yaw;
            pk.pitch = (float) this.pitch;
            pk.item = this.getInventory().getItemInHand();
            pk.metadata = this.dataProperties;
            player.dataPacket(pk);

            this.inventory.sendArmorContents(player);
            this.offhandInventory.sendContents(player);

            if (this.riding != null) {
                SetEntityLinkPacket pkk = new SetEntityLinkPacket();
                pkk.vehicleUniqueId = this.riding.getId();
                pkk.riderUniqueId = this.getId();
                pkk.type = 1;
                pkk.immediate = 1;

                player.dataPacket(pkk);
            }
            this.server.removePlayerListData(this.getUniqueId(), player);
        }
    }

    @Override
    public void despawnFrom(Player player) {
        if (this.hasSpawned.containsKey(player.getLoaderId())) {

            RemoveEntityPacket pk = new RemoveEntityPacket();
            pk.eid = this.getId();
            player.dataPacket(pk);
            this.hasSpawned.remove(player.getLoaderId());
        }
    }

    @Override
    public void close() {
        if (!this.closed) {
            if (inventory != null) {
                for (Player viewer : this.inventory.getViewers()) {
                    viewer.removeWindow(this.inventory);
                }
            }
            super.close();
        }
    }

    @PowerNukkitOnly
    @Override
    protected void onBlock(Entity entity, EntityDamageEvent event, boolean animate) {
        super.onBlock(entity, event, animate);
        Item shield = getInventory().getItemInHand();
        Item shieldOffhand = getOffhandInventory().getItem(0);
        if (shield instanceof ItemShield) {
            shield = damageArmor(shield, entity, event);
            getInventory().setItemInHand(shield);
        } else if (shieldOffhand instanceof ItemShield) {
            shieldOffhand = damageArmor(shieldOffhand, entity, event);
            getOffhandInventory().setItem(0, shieldOffhand);
        }
    }

    @Override
    public EntityArmorInventory getArmorInventory() {
        return null;
    }

    @Override
    public EntityEquipmentInventory getEquipmentInventory() {
        return null;
    }

    @PowerNukkitXOnly
    @Since("1.6.0.0-PNX")
    public Item getHelmet() {
        return this.getInventory().getHelmet();
    }

    @PowerNukkitXOnly
    @Since("1.6.0.0-PNX")
    public boolean setHelmet(Item item) {
        return this.getInventory().setHelmet(item);
    }

    @PowerNukkitXOnly
    @Since("1.6.0.0-PNX")
    public Item getChestplate() {
        return this.getInventory().getChestplate();
    }

    @PowerNukkitXOnly
    @Since("1.6.0.0-PNX")
    public boolean setChestplate(Item item) {
        return this.getInventory().setChestplate(item);
    }

    @PowerNukkitXOnly
    @Since("1.6.0.0-PNX")
    public Item getLeggings() {
        return getInventory().getLeggings();
    }

    @PowerNukkitXOnly
    @Since("1.6.0.0-PNX")
    public boolean setLeggings(Item item) {
        return getInventory().setLeggings(item);
    }

    @PowerNukkitXOnly
    @Since("1.6.0.0-PNX")
    public Item getBoots() {
        return getInventory().getBoots();
    }

    @PowerNukkitXOnly
    @Since("1.6.0.0-PNX")
    public boolean setBoots(Item item) {
        return getInventory().setBoots(item);
    }

    @PowerNukkitXOnly
    @Since("1.6.0.0-PNX")
    public Item getItemInHand() {
        return getInventory().getItemInHand();
    }

    @PowerNukkitXOnly
    @Since("1.19.60-r1")
    public Item getItemInOffhand() {
        return this.getOffhandInventory().getItem(0);
    }

    @PowerNukkitXOnly
    @Since("1.6.0.0-PNX")
    public boolean setItemInHand(Item item) {
        return getInventory().setItemInHand(item);
    }

    @PowerNukkitXOnly
    @Since("1.19.60-r1")
    public boolean setItemInHand(Item item, boolean send) {
        return this.getInventory().setItem(getInventory().getHeldItemIndex(), item, send);
    }

    @PowerNukkitXOnly
    @Since("1.19.60-r1")
    public boolean setItemInOffhand(Item item) {
        return this.getOffhandInventory().setItem(0, item, true);
    }

    @PowerNukkitXOnly
    @Since("1.19.60-r1")
    public boolean setItemInOffhand(Item item, boolean send) {
        return this.getOffhandInventory().setItem(0, item, send);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy