Jump to content

Recommended Posts

Posted (edited)

I'm trying to make a custom block that slows down mobs (definitely the player, possibly enemies, but not items). I first looked at the behavior of BlockWeb, but it hardcodes a field "isInWeb" in Entity, so I can't directly copy that block's behavior without modifying the vanilla code directly. After that, I looked into AttributeModifiers to see if I could modify "SharedMonsterAttributes.MOVEMENT_SPEED" inside of EntityLivingBase. My plan was to apply a custom slowness effect when inside the block, and try to remove it from the player every tick. Here's my current code:
 

    // Apply slowness when an EntityLivingBase is within the bounds of the sapling block.
    @Override
    public void onEntityCollision(IBlockState state, World worldIn, BlockPos pos, Entity entityIn) {
        if (entityIn instanceof EntityLivingBase) {
            IAttributeInstance movement = ((EntityLivingBase) entityIn).getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED);
            if (movement.hasModifier(saplingSlow)) {
                movement.removeModifier(saplingSlow);
            }
            movement.applyModifier(saplingSlow);
            System.out.println("Applied slowness");
        }
    }

    // Attempt to remove the effect when outside the bounds of the block using a PlayerTick event.
    // This is registered in a registration event elsewhere.
    @SubscribeEvent
    public static void onPlayerTick(TickEvent.PlayerTickEvent playerTickEvent) {
        if (playerTickEvent.side == LogicalSide.SERVER) {
            IAttributeInstance movement = playerTickEvent.player.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED);
            if (movement.getModifier(saplingSlowUUID) != null) {
                if (movement.hasModifier(saplingSlow)) {
                    movement.removeModifier(saplingSlow);
                }
            }
        }
    }


The "onEntityCollision" code works fine, but the "onPlayerTick" seems incorrect. When I walk into the block, the slowness is repeatedly applied and removed, rather than being smoothly active when inside the block and inactive when not. I could add logic to check if the Player is within the bounds of my custom block within the "onPlayerTick" method, but that doesn't seem like an efficient way of doing it. 

Is there an easier way to achieve what I'm trying to do? Or am I on the right track, but missing something in my current code? 

Edited by rushingseas8
Solved.
Posted
31 minutes ago, rushingseas8 said:

but it hardcodes a field "isInWeb" in Entity, so I can't directly copy that block's behavior without modifying the vanilla code directly

Yes you can. It isn't hardcoded, just call Entity#setInWeb.

 

Posted

I should also add that I don't want to have the hardcoded 0.25x multiplier on horizontal speed/0.05x on vertical. Ideally I'd want to have a 0.8x speed multipler across the board, which Entity#setInWeb wouldn't do.

Posted

Then use a PlayerTick event to check if a player is inside of your block and add the modifier to them if they are(and don't have a modifier) and remove it(if they have it) if they are not.

Worst case scenario you are checking 12 blocks each tich which is nothing considering the fast blockstate access and direct reference comparasons(==)

Posted

Okay, that seems to work. Shame there's not a more efficient way to do it. For reference, here's the working code:
 

    private static final UUID saplingSlowUUID = UUID.fromString("83BD3C05-50EB-460B-8961-615633A6D813");
    // -0.2D with operation 1 corresponds to a 0.8x speed multiplier.
    private static final AttributeModifier saplingSlow = new AttributeModifier(saplingSlowUUID, "SAPLING_SLOW", -0.2D, 1);

    @SubscribeEvent
    public static void onPlayerTick(TickEvent.PlayerTickEvent playerTickEvent) {
        if (playerTickEvent.side == LogicalSide.SERVER) {

            EntityPlayer player = playerTickEvent.player;
            World world = player.getEntityWorld();

            // Get all blocks that any part of the player model is colliding with.
            Iterable<BlockPos.MutableBlockPos> playerColliding = BlockPos.getAllInBoxMutable(
                    (int) Math.floor(player.posX - (player.width / 2)),
                    (int) Math.floor(player.posY - (player.height / 2)),
                    (int) Math.floor(player.posZ - (player.width / 2)),
                    (int) Math.floor(player.posX + (player.width / 2)),
                    (int) Math.floor(player.posY + (player.height / 2)),
                    (int) Math.floor(player.posZ + (player.width / 2))
            );

            boolean shouldSlow = false;
            for (BlockPos bp : playerColliding) {
                shouldSlow |= world.getBlockState(bp).getBlock() instanceof BlockSapling;
            }

            IAttributeInstance movement = playerTickEvent.player.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED);

            if (shouldSlow) {
                // Add modifier, if it's not already applied
                if (!movement.hasModifier(saplingSlow)) {
                    movement.applyModifier(saplingSlow);
                }
            } else {
                // Remove modifier, if it exists
                if (movement.hasModifier(saplingSlow)) {
                    movement.removeModifier(saplingSlow);
                }
            }
        }
    }

// [Elsewhere, in some register event]     
MinecraftForge.EVENT_BUS.register(BlockSapling.class);


Thanks for the help. If anyone has a better way to do this, please let me know.

Posted

Check also Soul Sand.

  • Like 1

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted

That works a lot better for my needs. The solution I previously had also messed with FOV (which might be useful for some other applications). Updated code:

    // BlockSapling.java
    @Override
    public void onEntityCollision(IBlockState state, World worldIn, BlockPos pos, Entity entityIn) {
        entityIn.motionX *= 0.8f;
        entityIn.motionY *= 0.8f;
        entityIn.motionZ *= 0.8f;
    }


Thanks a bunch!

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.