[neoforge [1.21.1] Вопрос по Хитбоксу игрока

Версия Minecraft
1.21.1
API
NeoForge
7
1
1
Я новичок в java, так что, если у вас горит сидалище, то объясните почему и где узнать столь очевидную инфу.
Добрый день. Хочу добавить эффект, который позволил бы ходить по воде так же как и по твёрдому блоку. Пример такого поведения Лавомерка или Running Shoes из Artifacts. У меня в голове логика следующая:
1. Отслеживаем, есть ли вода под игроком;
2. Выталкиваем игрока, если он ниже поверхности воды;
3. Поднимаем хитбокс игрока на 0.2, чтобы он не соприкосался с блоком воды, так как реально вода высотой 0.8, то я опускаю игрока на 0.2 ниже и из-за пересечения воды и хитбокса к игроку применяются эффекты (импульс и вязкость) будто бы я в воде.


Проблема в том, что хитбокс поднимается, но при любом движении становится стандартным и замедляет меня. Как жестко установить размер хитбокса или какую логику лучше использовать, чтобы достичь нужного эффекта?

Java:
public class WaterWalkEffect extends MobEffect {
    public WaterWalkEffect(MobEffectCategory category, int color) {
        super(category, color);
    }

    @Override
    public boolean applyEffectTick(LivingEntity livingEntity, int amplifier) {
        if (livingEntity instanceof Player player) {

            Level level = player.level();
            BlockPos belowPos = player.blockPosition();
            Minecraft mc = Minecraft.getInstance();

            boolean isOnWater = level.getBlockState(belowPos).is(Blocks.WATER)
                    && level.getBlockState(belowPos.above()).isAir()
                    && !player.isSwimming()
                    && !player.isShiftKeyDown()
                    && !mc.options.keyJump.isDown();

            if (isOnWater) {
                Vec3 velocity = player.getDeltaMovement();
                double waterSurface = belowPos.getY() + 0.8;

                if (player.getY() < waterSurface) {
                    player.setDeltaMovement(velocity.x, (waterSurface - player.getY()) * 0.2, velocity.z);
                }

                player.setOnGround(true);
                AABB newBox = new AABB(
                        player.getX() - 0.3, player.getY() + 0.2, player.getZ() - 0.3,  // Нижняя граница
                        player.getX() + 0.3, player.getY() + 1.8, player.getZ() + 0.3  // Верхняя граница
                );
                player.setBoundingBox(newBox);
                return true;
            }
        }
        return super.applyEffectTick(livingEntity, amplifier);
    }

    @Override
    public boolean shouldApplyEffectTickThisTick(int duration, int amplifier) {
        return true;
    }
}
 
Решение
Сам класс EntityDimensions можно наследовать
К сожалению, наследовать EntityDimensions нельзя так как он public record. Решение получилось следующим:
1. Через mixin меняю колизию воды, чтобы игра думала, что я на твердом блоке путем изменения LiquidBlock.getCollisionShape
2. Через mixin принудительно говорю игре, что игрок не находится в воде, чтобы не применялись эффекты замедления при пересечении хитбокса и блока воды, путем изменения Entity.isInWater и Entity.isInLiquid .

Там на самом деле все на много сложнее, но логика такая. Я создаю переменную в LiquidBlock, которую меняю через класс, где устанавливаю правила кастомного эффекта, который вешается на игрока. Если переменная...
Есть идея: найти событие, в котором пересчитывается хитбокс у сущностей и если эффект применен пересчитывать его так как мне нужно. Я не хочу использовать Mixin так как не знаю как он работает, а инфу найти не могу
 
Есть идея: найти событие, в котором пересчитывается хитбокс у сущностей и если эффект применен пересчитывать его так как мне нужно. Я не хочу использовать Mixin так как не знаю как он работает, а инфу найти не могу
Такого события нет
И если бы оно было бы - оно вызвало бы дичайшие лаги

Не знаю как на новых, но на старых версиях есть событие, которое вызывается когда что-то пытается рассчитать коллизии
Сущность (если есть), целевой хитбокс и результаты (если правильно помню)

Можно попробовать отменить событие и рассчитать коллизии вручную, однако в отличае от mojang-кода не игнорировать воду
 
но при любом движении становится стандартным
срабатывает makeBoundingBox из EntityDimensions.
С помощью AT или ранее упомянутых миксинов, можно cделать dimension публичным полем и на свой менять(настраивать можно только ширину и высоту).
 
Есть ивент PlayerTickEvent.Post. Положи этот код в этот ивент.
Результаты экспериментов. Внутри ивента хитбокс отказывает изменяться вовсе и игрок тонет стоя на воде. Добавил принудительную минхронизацию: player.hurtMarked = true; , но теперь игрок прыгает на месте
 
Но лучше пользоваться эвентом выше(для лучшей совместимости).
Все же придется использовать Mixin, так как иного выхода нет. Хитбокс игрока обновляется самой игрой (подтвердилось) и поменять его нельзя. Нууу… Попробую разобраться, если получится — напишу ответ
 
Хитбокс игрока обновляется самой игрой (подтвердилось) и поменять его нельзя. Нууу… Попробую разобраться, если получится — напишу ответ
Поменять можно. Выше написал.
С помощью AT или ранее упомянутых миксинов, можно cделать dimension публичным полем и на свой менять(настраивать можно только ширину и высоту).
Сам класс EntityDimensions можно наследовать, переопределить метод makeBoundingBox, и менять на этот класс в твоём applyEffectTick. Таким образом полноценный AABB с min и max.
Как будто бы сомнительный способ, но всё лучше чем миксины(моё мнение).
 
Сам класс EntityDimensions можно наследовать
К сожалению, наследовать EntityDimensions нельзя так как он public record. Решение получилось следующим:
1. Через mixin меняю колизию воды, чтобы игра думала, что я на твердом блоке путем изменения LiquidBlock.getCollisionShape
2. Через mixin принудительно говорю игре, что игрок не находится в воде, чтобы не применялись эффекты замедления при пересечении хитбокса и блока воды, путем изменения Entity.isInWater и Entity.isInLiquid .

Там на самом деле все на много сложнее, но логика такая. Я создаю переменную в LiquidBlock, которую меняю через класс, где устанавливаю правила кастомного эффекта, который вешается на игрока. Если переменная изменилась, то реализую повередние, если нет, то не реализую
 
Назад
Сверху