Jump to content

[Version 1.18.2, SOLVED] Add custom ambient sound to silent mobs (with a little help from capabilities)


Recommended Posts

Posted (edited)

Hello,

I am wondering how I would add ambient sounds to "silent" mobs (such as iron golems, snow golems, and players). I was looking at this forum and I have some questions...

Suppose I want to add ambient noises to iron golems. Do I have to use an event handler that takes in a TickEvent.ServerTickEvent argument? And In order to get all iron golems to be able to emit noises, how would I get a list of all registered iron golems?

Edited by LeeCrafts
  • LeeCrafts changed the title to [Version 1.18.2] Add custom ambient sound to (silent) mobs
Posted (edited)

Update: Don't use this solution. See my bottom comment for a solution involving capabilities.

 

My solution works, but for those who are asking the same questions, this is a bit more complex than we wish it to be.

If you want to add ambient noises to iron golems for example, you would first need to store each iron golem's relevant data in a list. The relevant data includes its UUID string and tick counter (an int value initialized at 0). I recommend using an extra class for this!

Now you must make two event handlers. One handles EntityJoinWorldEvent events, and it checks if event.getEntity() is an iron golem. If so, then add its data to the list. 

The other event handler handles LivingUpdateEvent events, which--as @diesieben07 mentioned--fires every tick (entities get updated every tick). Get the UUID of the entity being updated (event.getEntity().getStringUUID()), and search through the list to get the matching entity data, aka. the list entry containing the same UUID. Increment that entry's tick counter, and if it reaches an x amount of ticks, then play the noise and reset the counter.

 

Side note: Your LivingUpdateEvent handler probably should check if the entity (iron golem) has died, and if it has, then remove its respective entry from the list.

 

I hope my solution is not unnecessarily complicated.

Edit: maybe hash maps are more efficient than lists, but I’ll have to check and come back later. 

Edited by LeeCrafts
Posted (edited)

I just made a working solution with capabilities and it was one hell of a learning experience. There were many tutorials I had to follow because they were mostly from earlier versions. Below is an example code for anyone else who wants to add ambient noises to silent mobs--and learn how to use custom capabilities.

I attach a custom capability (TimeCounter) to iron golems, making each store their own time counter. The counter gets incremented every tick, and once it reaches a certain limit, the iron golem plays a sound and the counter is reset.

 

// must have an interface
public interface ITimeCounter {
    void incrementCounter();
    void resetCounter();
    void rollLimit();
}

 

// ...and a class implementing the interface
public class TimeCounter implements ITimeCounter {
    public int counter; // current tick counter
    public int limit; // number of ticks counter must reach for the sound to be played
    public TimeCounter() {
        counter = 0;
        rollLimit();
    }
    @Override
    public void incrementCounter() {
        this.counter++;
        if (counter < 0) resetCounter();
    }
    @Override
    public void resetCounter() { this.counter = 0; }
    @Override
    public void rollLimit() {
        // adds randomness to limit
        Random random = new Random();
        this.limit = random.nextInt(100) + 100;
    }
}

 

// provides the capability
public class TimeCounterProvider implements ICapabilitySerializable<CompoundTag> {

    private final TimeCounter timeCounter = new TimeCounter();
    private final LazyOptional<ITimeCounter> timeCounterLazyOptional = LazyOptional.of(() -> timeCounter);

    @NotNull
    @Override
    public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
        return timeCounterLazyOptional.cast();
    }

    // exclude serializeNBT() and deserializeNBT() if you don't need to persist the data
    // (in that case, also replace ICapabilitySerializable interface with ICapabilityProvider)
    @Override
    public CompoundTag serializeNBT() {
        if (ModCapabilities.TIME_COUNTER_CAPABILITY == null) {
            return new CompoundTag();
        }
        CompoundTag nbt = new CompoundTag();
        nbt.putInt("counter", timeCounter.counter);
        nbt.putInt("limit", timeCounter.limit);
        return nbt;
    }

    @Override
    public void deserializeNBT(CompoundTag nbt) {
        if (ModCapabilities.TIME_COUNTER_CAPABILITY != null) {
            timeCounter.counter = nbt.getInt("counter");
            timeCounter.limit = nbt.getInt("limit");
        }
    }

    // invalidates capability
    public void invalidate() {
        timeCounterLazyOptional.invalidate();
    }
}

 

public class ModCapabilities {

    // instance of the capability
    public static Capability<ITimeCounter> TIME_COUNTER_CAPABILITY = CapabilityManager.get(new CapabilityToken<>(){});
  
    // can put other capabilities here

}

 

@Mod.EventBusSubscriber(modid = MyMod.MOD_ID)
public class ModEvents {

    // registers the capability
    @SubscribeEvent
    public void registerCapabilities(RegisterCapabilitiesEvent event) {
        event.register(ITimeCounter.class);
    }

    // attaches the capability to iron golems
    @SubscribeEvent
    public static void onAttachCapabilitiesEvent(AttachCapabilitiesEvent<Entity> event) {
        if (event.getObject() instanceof IronGolem && !event.getObject().getCommandSenderWorld().isClientSide) {
            TimeCounterProvider timeCounterProvider = new TimeCounterProvider();
            event.addCapability(new ResourceLocation(MyMod.MOD_ID, "time_cap"), timeCounterProvider);
            event.addListener(timeCounterProvider::invalidate);
        }
    }

    // entities get updated every tick
    @SubscribeEvent
    public static void ironGolemSound(LivingEvent.LivingUpdateEvent event) {
        Entity entity = event.getEntity();
        if (entity instanceof IronGolem && !entity.level.isClientSide()) {
            entity.getCapability(ModCapabilities.TIME_COUNTER_CAPABILITY).ifPresent(iTimeCounter -> {
                TimeCounter timeCounter = (TimeCounter) iTimeCounter;
                timeCounter.incrementCounter();
                // when the counter reaches the limit, sound is played
                if (timeCounter.counter >= timeCounter.limit) {
                    entity.playSound(ModSounds.IRON_GOLEM_GRUNT.get(), 1.0F, 1.0F);
                    // entity.level.playSound(null, entity.getX(), entity.getY(), entity.getZ(), ModSounds.IRON_GOLEM_GRUNT.get(), SoundSource.AMBIENT, 1.0F, 1.0F);
                    timeCounter.resetCounter();
                    timeCounter.rollLimit();
                }
            });
        }
    }
}

 

Sources I mainly used: 

https://www.planetminecraft.com/blog/forge-tutorial-capability-system/

https://mcforge.readthedocs.io/en/1.18.x/datastorage/capabilities/

https://forums.minecraftforge.net/topic/106325-1171-forge-capability-how-to-save/

Edited by LeeCrafts
  • LeeCrafts changed the title to [Version 1.18.2, SOLVED] Add custom ambient sound to silent mobs (with a little help from capabilities)

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.