Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

Визуальная часть KillEffect | ExosWare 1 21 4

Начинающий
Начинающий
Статус
Оффлайн
Регистрация
25 Дек 2023
Сообщения
9
Реакции
0
Выберите загрузчик игры
  1. Fabric
Пр юг, простенький KillEffect для ексоса ( лень было делать чтото с рендером сделал просто из фееров крестик,доработать и +- норм, первые темы тут, жду del )
Видео ->
Пожалуйста, авторизуйтесь для просмотра ссылки.


GPT KillEffect:
Expand Collapse Copy
package ru.levin.modules.render;

import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.math.Vec3d;
import ru.levin.events.Event;
import ru.levin.events.impl.EventUpdate;
import ru.levin.modules.Function;
import ru.levin.modules.FunctionAnnotation;
import ru.levin.modules.Type;
import ru.levin.modules.setting.BooleanSetting;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@FunctionAnnotation(name = "KillEffect", keywords = "Death, Effect", type = Type.Render, desc = "Эффект креста при смерти")
public class KillEffect extends Function {

    private final BooleanSetting mobs = new BooleanSetting("Мобы", false);

    private final Map<String, DeathData> deaths = new ConcurrentHashMap<>();
    private final Map<String, Long> processedDeaths = new ConcurrentHashMap<>();

    public KillEffect() {
        addSettings(mobs);
    }

    @Override
    public void onEnable() {
        System.out.println("§a[KillEffect] Модуль включен!");
    }

    @Override
    public void onDisable() {
        deaths.clear();
        processedDeaths.clear();
    }

    @Override
    public void onEvent(Event event) {
        if (mc.world == null || mc.player == null) return;

        if (event instanceof EventUpdate) {
            for (Entity entity : mc.world.getEntities()) {
                if (!(entity instanceof LivingEntity living)) continue;
                if (entity == mc.player) continue;

                float health = living.getHealth();
                String entityKey = entity.getUuid().toString();

                if (health <= 0) {
                    if (!processedDeaths.containsKey(entityKey)) {
                        boolean isPlayer = entity instanceof PlayerEntity;
                        boolean shouldAdd = mobs.get() || isPlayer;

                        if (shouldAdd) {
                            addDeathEffect(entity);
                            processedDeaths.put(entityKey, System.currentTimeMillis());
                        }
                    }
                } else {
                    processedDeaths.remove(entityKey);
                }
            }

            deaths.entrySet().removeIf(entry -> System.currentTimeMillis() - entry.getValue().getTime() > 3000);
            processedDeaths.entrySet().removeIf(entry -> System.currentTimeMillis() - entry.getValue() > 5000);

            renderEffects();
        }
    }

    private void renderEffects() {
        if (deaths.isEmpty()) return;

        long currentTime = System.currentTimeMillis();

        for (DeathData data : deaths.values()) {
            float progress = Math.min(1f, (currentTime - data.getTime()) / 3000f);
            Vec3d pos = data.getPos();

            drawCross(pos, progress);
        }
    }

    private void drawCross(Vec3d pos, float progress) {
        double centerX = pos.x;
        double centerY = pos.y + 1.5;
        double centerZ = pos.z;

        // Получаем направление от креста к игроку (камера)
        double cameraX = mc.player.getX();
        double cameraZ = mc.player.getZ();
        double dx = centerX - cameraX;
        double dz = centerZ - cameraZ;
        double angleToPlayer = Math.atan2(dz, dx);

        double armLength = 0.7;
        float brightness = 1f - progress * 0.6f;

        // РАСПАД НА ЧАСТИЦЫ (когда progress > 0.85)
        if (progress > 0.85f) {
            float decayProgress = (progress - 0.85f) / 0.15f;
            int particleCount = (int) (200 * decayProgress);

            for (int i = 0; i < particleCount; i++) {
                // Случайное направление разлета
                double angle = Math.random() * Math.PI * 2;
                double pitch = Math.random() * Math.PI * 2;
                double radius = 1.0 * decayProgress;

                // Позиция разлета (от центра креста)
                double x = centerX + Math.cos(angle) * Math.cos(pitch) * radius;
                double y = centerY + Math.sin(pitch) * radius;
                double z = centerZ + Math.sin(angle) * Math.cos(pitch) * radius;

                // Скорость разлета
                double vx = (x - centerX) * 0.2;
                double vy = (y - centerY) * 0.2 + 0.05;
                double vz = (z - centerZ) * 0.2;

                // Разные типы частиц для красивого распада
                if (Math.random() < 0.6) {
                    mc.world.addParticle(ParticleTypes.END_ROD, x, y, z, vx, vy, vz);
                } else {
                    mc.world.addParticle(ParticleTypes.GLOW, x, y, z, vx, vy, vz);
                }
            }
            return;
        }

        // Нормальный рендер креста (оба луча повернуты к игроку)
        float flicker = 0.8f + (float) (Math.sin(System.currentTimeMillis() / 120.0) * 0.2f) * brightness;
        int particleCount = (int) (35 * brightness);

        // ВЕРТИКАЛЬНАЯ ПАЛКА (теперь тоже поворачивается к игроку)
        for (int i = 0; i <= particleCount; i++) {
            double t = (double) i / particleCount;
            double offset = -armLength + t * armLength * 2;

            // Поворачиваем вертикальную палку вверх-вниз, но она должна оставаться вертикальной?
            // Нет, для 2D билборда нужно повернуть оба луча к камере
            // Вертикальная палка в 2D билборде - это линия вверх-вниз в плоскости камеры
            // Для этого используем два направления: вверх (0,1,0) и в сторону камеры

            // Вектор "вверх" в мировой системе координат
            double upX = 0;
            double upY = 1;
            double upZ = 0;

            // Вектор "вправо" (перпендикулярно направлению к камере)
            double rightX = Math.cos(angleToPlayer);
            double rightZ = Math.sin(angleToPlayer);
            double rightY = 0;

            // Нормализуем
            double len = Math.sqrt(rightX * rightX + rightZ * rightZ);
            rightX /= len;
            rightZ /= len;

            // Вертикальная палка: смещение по вектору UP
            double x = centerX + upX * offset;
            double y = centerY + upY * offset;
            double z = centerZ + upZ * offset;

            mc.world.addParticle(ParticleTypes.END_ROD, x, y, z, 0, 0, 0);

            if (brightness > 0.5f && Math.random() < 0.2f) {
                mc.world.addParticle(ParticleTypes.GLOW,
                        x + (Math.random() - 0.5) * 0.08,
                        y + (Math.random() - 0.5) * 0.08,
                        z + (Math.random() - 0.5) * 0.08,
                        0, 0, 0);
            }
        }

        // ГОРИЗОНТАЛЬНАЯ ПАЛКА (поворачивается к игроку)
        for (int i = 0; i <= particleCount; i++) {
            double t = (double) i / particleCount;
            double offset = -armLength + t * armLength * 2;

            // Вектор "вправо" (перпендикулярно направлению к камере)
            double rightX = Math.cos(angleToPlayer);
            double rightZ = Math.sin(angleToPlayer);
            double rightY = 0;

            // Нормализуем
            double len = Math.sqrt(rightX * rightX + rightZ * rightZ);
            rightX /= len;
            rightZ /= len;

            // Горизонтальная палка: смещение по вектору RIGHT
            double x = centerX + rightX * offset;
            double y = centerY;
            double z = centerZ + rightZ * offset;

            mc.world.addParticle(ParticleTypes.END_ROD, x, y, z, 0, 0, 0);

            if (brightness > 0.5f && Math.random() < 0.2f) {
                mc.world.addParticle(ParticleTypes.GLOW,
                        x + (Math.random() - 0.5) * 0.08,
                        y + (Math.random() - 0.5) * 0.08,
                        z + (Math.random() - 0.5) * 0.08,
                        0, 0, 0);
            }
        }

        // Свечение на пересечении
        for (int i = 0; i < 12; i++) {
            mc.world.addParticle(ParticleTypes.GLOW,
                    centerX + (Math.random() - 0.5) * 0.25,
                    centerY + (Math.random() - 0.5) * 0.25,
                    centerZ + (Math.random() - 0.5) * 0.25,
                    0, 0, 0);
        }

        // Парящие частицы вокруг креста (вращаются)
        if (brightness > 0.6f) {
            for (int i = 0; i < 10; i++) {
                double angle = Math.toRadians(i * 36 + System.currentTimeMillis() / 12.0);
                double radius = 0.55;
                double x = centerX + Math.cos(angle) * radius;
                double z = centerZ + Math.sin(angle) * radius;
                double y = centerY + Math.sin(angle * 2) * 0.2;

                mc.world.addParticle(ParticleTypes.ELECTRIC_SPARK, x, y, z, 0, 0.01, 0);
            }
        }

        // Искры на концах палок (только в начале)
        if (progress < 0.35f) {
            double endYTop = centerY + armLength;
            double endYBottom = centerY - armLength;
            double endXRight = centerX + Math.cos(angleToPlayer) * armLength;
            double endZRight = centerZ + Math.sin(angleToPlayer) * armLength;
            double endXLeft = centerX - Math.cos(angleToPlayer) * armLength;
            double endZLeft = centerZ - Math.sin(angleToPlayer) * armLength;

            for (int i = 0; i < 6; i++) {
                mc.world.addParticle(ParticleTypes.ELECTRIC_SPARK, endXLeft, centerY, endZLeft, (Math.random() - 0.5) * 0.08, (Math.random() - 0.5) * 0.08, (Math.random() - 0.5) * 0.08);
                mc.world.addParticle(ParticleTypes.ELECTRIC_SPARK, endXRight, centerY, endZRight, (Math.random() - 0.5) * 0.08, (Math.random() - 0.5) * 0.08, (Math.random() - 0.5) * 0.08);
                mc.world.addParticle(ParticleTypes.ELECTRIC_SPARK, centerX, endYTop, centerZ, (Math.random() - 0.5) * 0.08, 0.08, (Math.random() - 0.5) * 0.08);
                mc.world.addParticle(ParticleTypes.ELECTRIC_SPARK, centerX, endYBottom, centerZ, (Math.random() - 0.5) * 0.08, -0.08, (Math.random() - 0.5) * 0.08);
            }
        }
    }

    public void addDeathEffect(Entity entity) {
        System.out.println("§e[KillEffect] Смерть: " + entity.getName().getString());

        mc.world.playSound(mc.player, entity.getBlockPos(),
                SoundEvents.ENTITY_PLAYER_DEATH,
                SoundCategory.PLAYERS,
                1.0f, 1.0f);

        mc.world.playSound(mc.player, entity.getBlockPos(),
                SoundEvents.BLOCK_BEACON_ACTIVATE,
                SoundCategory.PLAYERS,
                0.8f, 1.2f);

        deaths.put(entity.getUuid().toString(), new DeathData(System.currentTimeMillis(), entity.getPos()));
    }

    private static class DeathData {
        private final long time;
        private final Vec3d pos;

        public DeathData(long time, Vec3d pos) {
            this.time = time;
            this.pos = pos;
        }

        public long getTime() { return time; }
        public Vec3d getPos() { return pos; }
    }
}
 
Пр юг, простенький KillEffect для ексоса ( лень было делать чтото с рендером сделал просто из фееров крестик,доработать и +- норм, первые темы тут, жду del )
Видео ->
Пожалуйста, авторизуйтесь для просмотра ссылки.


GPT KillEffect:
Expand Collapse Copy
package ru.levin.modules.render;

import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.math.Vec3d;
import ru.levin.events.Event;
import ru.levin.events.impl.EventUpdate;
import ru.levin.modules.Function;
import ru.levin.modules.FunctionAnnotation;
import ru.levin.modules.Type;
import ru.levin.modules.setting.BooleanSetting;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@FunctionAnnotation(name = "KillEffect", keywords = "Death, Effect", type = Type.Render, desc = "Эффект креста при смерти")
public class KillEffect extends Function {

    private final BooleanSetting mobs = new BooleanSetting("Мобы", false);

    private final Map<String, DeathData> deaths = new ConcurrentHashMap<>();
    private final Map<String, Long> processedDeaths = new ConcurrentHashMap<>();

    public KillEffect() {
        addSettings(mobs);
    }

    @Override
    public void onEnable() {
        System.out.println("§a[KillEffect] Модуль включен!");
    }

    @Override
    public void onDisable() {
        deaths.clear();
        processedDeaths.clear();
    }

    @Override
    public void onEvent(Event event) {
        if (mc.world == null || mc.player == null) return;

        if (event instanceof EventUpdate) {
            for (Entity entity : mc.world.getEntities()) {
                if (!(entity instanceof LivingEntity living)) continue;
                if (entity == mc.player) continue;

                float health = living.getHealth();
                String entityKey = entity.getUuid().toString();

                if (health <= 0) {
                    if (!processedDeaths.containsKey(entityKey)) {
                        boolean isPlayer = entity instanceof PlayerEntity;
                        boolean shouldAdd = mobs.get() || isPlayer;

                        if (shouldAdd) {
                            addDeathEffect(entity);
                            processedDeaths.put(entityKey, System.currentTimeMillis());
                        }
                    }
                } else {
                    processedDeaths.remove(entityKey);
                }
            }

            deaths.entrySet().removeIf(entry -> System.currentTimeMillis() - entry.getValue().getTime() > 3000);
            processedDeaths.entrySet().removeIf(entry -> System.currentTimeMillis() - entry.getValue() > 5000);

            renderEffects();
        }
    }

    private void renderEffects() {
        if (deaths.isEmpty()) return;

        long currentTime = System.currentTimeMillis();

        for (DeathData data : deaths.values()) {
            float progress = Math.min(1f, (currentTime - data.getTime()) / 3000f);
            Vec3d pos = data.getPos();

            drawCross(pos, progress);
        }
    }

    private void drawCross(Vec3d pos, float progress) {
        double centerX = pos.x;
        double centerY = pos.y + 1.5;
        double centerZ = pos.z;

        // Получаем направление от креста к игроку (камера)
        double cameraX = mc.player.getX();
        double cameraZ = mc.player.getZ();
        double dx = centerX - cameraX;
        double dz = centerZ - cameraZ;
        double angleToPlayer = Math.atan2(dz, dx);

        double armLength = 0.7;
        float brightness = 1f - progress * 0.6f;

        // РАСПАД НА ЧАСТИЦЫ (когда progress > 0.85)
        if (progress > 0.85f) {
            float decayProgress = (progress - 0.85f) / 0.15f;
            int particleCount = (int) (200 * decayProgress);

            for (int i = 0; i < particleCount; i++) {
                // Случайное направление разлета
                double angle = Math.random() * Math.PI * 2;
                double pitch = Math.random() * Math.PI * 2;
                double radius = 1.0 * decayProgress;

                // Позиция разлета (от центра креста)
                double x = centerX + Math.cos(angle) * Math.cos(pitch) * radius;
                double y = centerY + Math.sin(pitch) * radius;
                double z = centerZ + Math.sin(angle) * Math.cos(pitch) * radius;

                // Скорость разлета
                double vx = (x - centerX) * 0.2;
                double vy = (y - centerY) * 0.2 + 0.05;
                double vz = (z - centerZ) * 0.2;

                // Разные типы частиц для красивого распада
                if (Math.random() < 0.6) {
                    mc.world.addParticle(ParticleTypes.END_ROD, x, y, z, vx, vy, vz);
                } else {
                    mc.world.addParticle(ParticleTypes.GLOW, x, y, z, vx, vy, vz);
                }
            }
            return;
        }

        // Нормальный рендер креста (оба луча повернуты к игроку)
        float flicker = 0.8f + (float) (Math.sin(System.currentTimeMillis() / 120.0) * 0.2f) * brightness;
        int particleCount = (int) (35 * brightness);

        // ВЕРТИКАЛЬНАЯ ПАЛКА (теперь тоже поворачивается к игроку)
        for (int i = 0; i <= particleCount; i++) {
            double t = (double) i / particleCount;
            double offset = -armLength + t * armLength * 2;

            // Поворачиваем вертикальную палку вверх-вниз, но она должна оставаться вертикальной?
            // Нет, для 2D билборда нужно повернуть оба луча к камере
            // Вертикальная палка в 2D билборде - это линия вверх-вниз в плоскости камеры
            // Для этого используем два направления: вверх (0,1,0) и в сторону камеры

            // Вектор "вверх" в мировой системе координат
            double upX = 0;
            double upY = 1;
            double upZ = 0;

            // Вектор "вправо" (перпендикулярно направлению к камере)
            double rightX = Math.cos(angleToPlayer);
            double rightZ = Math.sin(angleToPlayer);
            double rightY = 0;

            // Нормализуем
            double len = Math.sqrt(rightX * rightX + rightZ * rightZ);
            rightX /= len;
            rightZ /= len;

            // Вертикальная палка: смещение по вектору UP
            double x = centerX + upX * offset;
            double y = centerY + upY * offset;
            double z = centerZ + upZ * offset;

            mc.world.addParticle(ParticleTypes.END_ROD, x, y, z, 0, 0, 0);

            if (brightness > 0.5f && Math.random() < 0.2f) {
                mc.world.addParticle(ParticleTypes.GLOW,
                        x + (Math.random() - 0.5) * 0.08,
                        y + (Math.random() - 0.5) * 0.08,
                        z + (Math.random() - 0.5) * 0.08,
                        0, 0, 0);
            }
        }

        // ГОРИЗОНТАЛЬНАЯ ПАЛКА (поворачивается к игроку)
        for (int i = 0; i <= particleCount; i++) {
            double t = (double) i / particleCount;
            double offset = -armLength + t * armLength * 2;

            // Вектор "вправо" (перпендикулярно направлению к камере)
            double rightX = Math.cos(angleToPlayer);
            double rightZ = Math.sin(angleToPlayer);
            double rightY = 0;

            // Нормализуем
            double len = Math.sqrt(rightX * rightX + rightZ * rightZ);
            rightX /= len;
            rightZ /= len;

            // Горизонтальная палка: смещение по вектору RIGHT
            double x = centerX + rightX * offset;
            double y = centerY;
            double z = centerZ + rightZ * offset;

            mc.world.addParticle(ParticleTypes.END_ROD, x, y, z, 0, 0, 0);

            if (brightness > 0.5f && Math.random() < 0.2f) {
                mc.world.addParticle(ParticleTypes.GLOW,
                        x + (Math.random() - 0.5) * 0.08,
                        y + (Math.random() - 0.5) * 0.08,
                        z + (Math.random() - 0.5) * 0.08,
                        0, 0, 0);
            }
        }

        // Свечение на пересечении
        for (int i = 0; i < 12; i++) {
            mc.world.addParticle(ParticleTypes.GLOW,
                    centerX + (Math.random() - 0.5) * 0.25,
                    centerY + (Math.random() - 0.5) * 0.25,
                    centerZ + (Math.random() - 0.5) * 0.25,
                    0, 0, 0);
        }

        // Парящие частицы вокруг креста (вращаются)
        if (brightness > 0.6f) {
            for (int i = 0; i < 10; i++) {
                double angle = Math.toRadians(i * 36 + System.currentTimeMillis() / 12.0);
                double radius = 0.55;
                double x = centerX + Math.cos(angle) * radius;
                double z = centerZ + Math.sin(angle) * radius;
                double y = centerY + Math.sin(angle * 2) * 0.2;

                mc.world.addParticle(ParticleTypes.ELECTRIC_SPARK, x, y, z, 0, 0.01, 0);
            }
        }

        // Искры на концах палок (только в начале)
        if (progress < 0.35f) {
            double endYTop = centerY + armLength;
            double endYBottom = centerY - armLength;
            double endXRight = centerX + Math.cos(angleToPlayer) * armLength;
            double endZRight = centerZ + Math.sin(angleToPlayer) * armLength;
            double endXLeft = centerX - Math.cos(angleToPlayer) * armLength;
            double endZLeft = centerZ - Math.sin(angleToPlayer) * armLength;

            for (int i = 0; i < 6; i++) {
                mc.world.addParticle(ParticleTypes.ELECTRIC_SPARK, endXLeft, centerY, endZLeft, (Math.random() - 0.5) * 0.08, (Math.random() - 0.5) * 0.08, (Math.random() - 0.5) * 0.08);
                mc.world.addParticle(ParticleTypes.ELECTRIC_SPARK, endXRight, centerY, endZRight, (Math.random() - 0.5) * 0.08, (Math.random() - 0.5) * 0.08, (Math.random() - 0.5) * 0.08);
                mc.world.addParticle(ParticleTypes.ELECTRIC_SPARK, centerX, endYTop, centerZ, (Math.random() - 0.5) * 0.08, 0.08, (Math.random() - 0.5) * 0.08);
                mc.world.addParticle(ParticleTypes.ELECTRIC_SPARK, centerX, endYBottom, centerZ, (Math.random() - 0.5) * 0.08, -0.08, (Math.random() - 0.5) * 0.08);
            }
        }
    }

    public void addDeathEffect(Entity entity) {
        System.out.println("§e[KillEffect] Смерть: " + entity.getName().getString());

        mc.world.playSound(mc.player, entity.getBlockPos(),
                SoundEvents.ENTITY_PLAYER_DEATH,
                SoundCategory.PLAYERS,
                1.0f, 1.0f);

        mc.world.playSound(mc.player, entity.getBlockPos(),
                SoundEvents.BLOCK_BEACON_ACTIVATE,
                SoundCategory.PLAYERS,
                0.8f, 1.2f);

        deaths.put(entity.getUuid().toString(), new DeathData(System.currentTimeMillis(), entity.getPos()));
    }

    private static class DeathData {
        private final long time;
        private final Vec3d pos;

        public DeathData(long time, Vec3d pos) {
            this.time = time;
            this.pos = pos;
        }

        public long getTime() { return time; }
        public Vec3d getPos() { return pos; }
    }
}
1774772794758.png
ох.... FURI BETA
 
Пр юг, простенький KillEffect для ексоса ( лень было делать чтото с рендером сделал просто из фееров крестик,доработать и +- норм, первые темы тут, жду del )
Видео ->
Пожалуйста, авторизуйтесь для просмотра ссылки.


GPT KillEffect:
Expand Collapse Copy
package ru.levin.modules.render;

import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.math.Vec3d;
import ru.levin.events.Event;
import ru.levin.events.impl.EventUpdate;
import ru.levin.modules.Function;
import ru.levin.modules.FunctionAnnotation;
import ru.levin.modules.Type;
import ru.levin.modules.setting.BooleanSetting;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@FunctionAnnotation(name = "KillEffect", keywords = "Death, Effect", type = Type.Render, desc = "Эффект креста при смерти")
public class KillEffect extends Function {

    private final BooleanSetting mobs = new BooleanSetting("Мобы", false);

    private final Map<String, DeathData> deaths = new ConcurrentHashMap<>();
    private final Map<String, Long> processedDeaths = new ConcurrentHashMap<>();

    public KillEffect() {
        addSettings(mobs);
    }

    @Override
    public void onEnable() {
        System.out.println("§a[KillEffect] Модуль включен!");
    }

    @Override
    public void onDisable() {
        deaths.clear();
        processedDeaths.clear();
    }

    @Override
    public void onEvent(Event event) {
        if (mc.world == null || mc.player == null) return;

        if (event instanceof EventUpdate) {
            for (Entity entity : mc.world.getEntities()) {
                if (!(entity instanceof LivingEntity living)) continue;
                if (entity == mc.player) continue;

                float health = living.getHealth();
                String entityKey = entity.getUuid().toString();

                if (health <= 0) {
                    if (!processedDeaths.containsKey(entityKey)) {
                        boolean isPlayer = entity instanceof PlayerEntity;
                        boolean shouldAdd = mobs.get() || isPlayer;

                        if (shouldAdd) {
                            addDeathEffect(entity);
                            processedDeaths.put(entityKey, System.currentTimeMillis());
                        }
                    }
                } else {
                    processedDeaths.remove(entityKey);
                }
            }

            deaths.entrySet().removeIf(entry -> System.currentTimeMillis() - entry.getValue().getTime() > 3000);
            processedDeaths.entrySet().removeIf(entry -> System.currentTimeMillis() - entry.getValue() > 5000);

            renderEffects();
        }
    }

    private void renderEffects() {
        if (deaths.isEmpty()) return;

        long currentTime = System.currentTimeMillis();

        for (DeathData data : deaths.values()) {
            float progress = Math.min(1f, (currentTime - data.getTime()) / 3000f);
            Vec3d pos = data.getPos();

            drawCross(pos, progress);
        }
    }

    private void drawCross(Vec3d pos, float progress) {
        double centerX = pos.x;
        double centerY = pos.y + 1.5;
        double centerZ = pos.z;

        // Получаем направление от креста к игроку (камера)
        double cameraX = mc.player.getX();
        double cameraZ = mc.player.getZ();
        double dx = centerX - cameraX;
        double dz = centerZ - cameraZ;
        double angleToPlayer = Math.atan2(dz, dx);

        double armLength = 0.7;
        float brightness = 1f - progress * 0.6f;

        // РАСПАД НА ЧАСТИЦЫ (когда progress > 0.85)
        if (progress > 0.85f) {
            float decayProgress = (progress - 0.85f) / 0.15f;
            int particleCount = (int) (200 * decayProgress);

            for (int i = 0; i < particleCount; i++) {
                // Случайное направление разлета
                double angle = Math.random() * Math.PI * 2;
                double pitch = Math.random() * Math.PI * 2;
                double radius = 1.0 * decayProgress;

                // Позиция разлета (от центра креста)
                double x = centerX + Math.cos(angle) * Math.cos(pitch) * radius;
                double y = centerY + Math.sin(pitch) * radius;
                double z = centerZ + Math.sin(angle) * Math.cos(pitch) * radius;

                // Скорость разлета
                double vx = (x - centerX) * 0.2;
                double vy = (y - centerY) * 0.2 + 0.05;
                double vz = (z - centerZ) * 0.2;

                // Разные типы частиц для красивого распада
                if (Math.random() < 0.6) {
                    mc.world.addParticle(ParticleTypes.END_ROD, x, y, z, vx, vy, vz);
                } else {
                    mc.world.addParticle(ParticleTypes.GLOW, x, y, z, vx, vy, vz);
                }
            }
            return;
        }

        // Нормальный рендер креста (оба луча повернуты к игроку)
        float flicker = 0.8f + (float) (Math.sin(System.currentTimeMillis() / 120.0) * 0.2f) * brightness;
        int particleCount = (int) (35 * brightness);

        // ВЕРТИКАЛЬНАЯ ПАЛКА (теперь тоже поворачивается к игроку)
        for (int i = 0; i <= particleCount; i++) {
            double t = (double) i / particleCount;
            double offset = -armLength + t * armLength * 2;

            // Поворачиваем вертикальную палку вверх-вниз, но она должна оставаться вертикальной?
            // Нет, для 2D билборда нужно повернуть оба луча к камере
            // Вертикальная палка в 2D билборде - это линия вверх-вниз в плоскости камеры
            // Для этого используем два направления: вверх (0,1,0) и в сторону камеры

            // Вектор "вверх" в мировой системе координат
            double upX = 0;
            double upY = 1;
            double upZ = 0;

            // Вектор "вправо" (перпендикулярно направлению к камере)
            double rightX = Math.cos(angleToPlayer);
            double rightZ = Math.sin(angleToPlayer);
            double rightY = 0;

            // Нормализуем
            double len = Math.sqrt(rightX * rightX + rightZ * rightZ);
            rightX /= len;
            rightZ /= len;

            // Вертикальная палка: смещение по вектору UP
            double x = centerX + upX * offset;
            double y = centerY + upY * offset;
            double z = centerZ + upZ * offset;

            mc.world.addParticle(ParticleTypes.END_ROD, x, y, z, 0, 0, 0);

            if (brightness > 0.5f && Math.random() < 0.2f) {
                mc.world.addParticle(ParticleTypes.GLOW,
                        x + (Math.random() - 0.5) * 0.08,
                        y + (Math.random() - 0.5) * 0.08,
                        z + (Math.random() - 0.5) * 0.08,
                        0, 0, 0);
            }
        }

        // ГОРИЗОНТАЛЬНАЯ ПАЛКА (поворачивается к игроку)
        for (int i = 0; i <= particleCount; i++) {
            double t = (double) i / particleCount;
            double offset = -armLength + t * armLength * 2;

            // Вектор "вправо" (перпендикулярно направлению к камере)
            double rightX = Math.cos(angleToPlayer);
            double rightZ = Math.sin(angleToPlayer);
            double rightY = 0;

            // Нормализуем
            double len = Math.sqrt(rightX * rightX + rightZ * rightZ);
            rightX /= len;
            rightZ /= len;

            // Горизонтальная палка: смещение по вектору RIGHT
            double x = centerX + rightX * offset;
            double y = centerY;
            double z = centerZ + rightZ * offset;

            mc.world.addParticle(ParticleTypes.END_ROD, x, y, z, 0, 0, 0);

            if (brightness > 0.5f && Math.random() < 0.2f) {
                mc.world.addParticle(ParticleTypes.GLOW,
                        x + (Math.random() - 0.5) * 0.08,
                        y + (Math.random() - 0.5) * 0.08,
                        z + (Math.random() - 0.5) * 0.08,
                        0, 0, 0);
            }
        }

        // Свечение на пересечении
        for (int i = 0; i < 12; i++) {
            mc.world.addParticle(ParticleTypes.GLOW,
                    centerX + (Math.random() - 0.5) * 0.25,
                    centerY + (Math.random() - 0.5) * 0.25,
                    centerZ + (Math.random() - 0.5) * 0.25,
                    0, 0, 0);
        }

        // Парящие частицы вокруг креста (вращаются)
        if (brightness > 0.6f) {
            for (int i = 0; i < 10; i++) {
                double angle = Math.toRadians(i * 36 + System.currentTimeMillis() / 12.0);
                double radius = 0.55;
                double x = centerX + Math.cos(angle) * radius;
                double z = centerZ + Math.sin(angle) * radius;
                double y = centerY + Math.sin(angle * 2) * 0.2;

                mc.world.addParticle(ParticleTypes.ELECTRIC_SPARK, x, y, z, 0, 0.01, 0);
            }
        }

        // Искры на концах палок (только в начале)
        if (progress < 0.35f) {
            double endYTop = centerY + armLength;
            double endYBottom = centerY - armLength;
            double endXRight = centerX + Math.cos(angleToPlayer) * armLength;
            double endZRight = centerZ + Math.sin(angleToPlayer) * armLength;
            double endXLeft = centerX - Math.cos(angleToPlayer) * armLength;
            double endZLeft = centerZ - Math.sin(angleToPlayer) * armLength;

            for (int i = 0; i < 6; i++) {
                mc.world.addParticle(ParticleTypes.ELECTRIC_SPARK, endXLeft, centerY, endZLeft, (Math.random() - 0.5) * 0.08, (Math.random() - 0.5) * 0.08, (Math.random() - 0.5) * 0.08);
                mc.world.addParticle(ParticleTypes.ELECTRIC_SPARK, endXRight, centerY, endZRight, (Math.random() - 0.5) * 0.08, (Math.random() - 0.5) * 0.08, (Math.random() - 0.5) * 0.08);
                mc.world.addParticle(ParticleTypes.ELECTRIC_SPARK, centerX, endYTop, centerZ, (Math.random() - 0.5) * 0.08, 0.08, (Math.random() - 0.5) * 0.08);
                mc.world.addParticle(ParticleTypes.ELECTRIC_SPARK, centerX, endYBottom, centerZ, (Math.random() - 0.5) * 0.08, -0.08, (Math.random() - 0.5) * 0.08);
            }
        }
    }

    public void addDeathEffect(Entity entity) {
        System.out.println("§e[KillEffect] Смерть: " + entity.getName().getString());

        mc.world.playSound(mc.player, entity.getBlockPos(),
                SoundEvents.ENTITY_PLAYER_DEATH,
                SoundCategory.PLAYERS,
                1.0f, 1.0f);

        mc.world.playSound(mc.player, entity.getBlockPos(),
                SoundEvents.BLOCK_BEACON_ACTIVATE,
                SoundCategory.PLAYERS,
                0.8f, 1.2f);

        deaths.put(entity.getUuid().toString(), new DeathData(System.currentTimeMillis(), entity.getPos()));
    }

    private static class DeathData {
        private final long time;
        private final Vec3d pos;

        public DeathData(long time, Vec3d pos) {
            this.time = time;
            this.pos = pos;
        }

        public long getTime() { return time; }
        public Vec3d getPos() { return pos; }
    }
}
/del #теория майнкрафт рендера
 
Назад
Сверху Снизу