Cobblemon Ambient NPC Behaviours
Three "atmosphere" behaviours for Cobblemon NPCs, plus an invisible Ambient preset that can easily uplift any scene. On their own, NPCs stand still and silent, so my goal for these behaviours was to add subtle sound and visual effects that make a setting feel more lively. Each one is added to an NPC, toggled on, and configured from the in-game editor (/npcedit) without writing any scripts.
Ambient Sound Loop
Plays an ambient sound on a configured interval, broadcast from the NPC's position so players nearby hear it with proper directional falloff. Up to three sound ids can be configured (ambient_sound_id_1 through _3), and each fire picks one non-blank slot at random. Volume and pitch are each rolled per fire between a configurable min and max, so a repeated clip does not sound mechanical.
A few details that make it usable in practice:
- Sound channel:
ambient_sound_channel(defaultambient) sets which volume slider the sound obeys, so an effect can route throughblock,music, orvoiceinstead. - Interval jitter: the gap is
ambient_sound_interval_ticksplus a random0..ambient_sound_jitter_ticks. As such, two identical NPCs placed together drift out of sync instead of chorusing. - Activation radius: the sound broadcasts to players within the engine's fixed 16-block radius, so
ambient_sound_player_radiusis an activation gate rather than the audible distance. It defaults to 16 to keep the gate aligned with who can actually hear it.
Idle Animation Cycler
Periodically plays a weighted random idle animation (wave, look_around, stretch, yawn, or whatever the model defines) so an NPC that is standing around does something every so often. Up to five animation slots are configurable, each with its own pick weight (idle_anim_weight_N), and each fire runs a cumulative weighted roll over the non-blank slots. The gap is a random value between idle_anim_interval_min_ticks and idle_anim_interval_max_ticks.
A busy gate keeps the cycler out of the way of anything more important. It checks three cheap conditions before firing and skips the cycle if any is true:
q.entity.has_walk_target: the NPC is walking somewhere.q.entity.is_in_dialogue(): a player is talking to it.q.entity.is_in_battle(): it is mid-battle.
As such, this behaviour composes well with moves_to_players, stays_at_current_position, and the Dialogue Behaviours, so the NPC tracks the player, emotes between conversations, and goes still the moment someone interacts.
Ambient Particle Emitter
Emits a bedrock (snowstorm) particle effect on a configurable interval from a point near the NPC. Up to three particle ids can be configured (ambient_particles_id_1 through _3), one picked at random each fire. The emission point is an offset (ambient_particles_x/y/z) from the NPC's current position, so the cloud follows a moving NPC, and the default y = 1.0 lifts particles off the feet to around body height.
The underlying spawn call takes a single point, with no native count or spread, so two parameters are layered on top in Molang:
- Count:
ambient_particles_countspawns that many instances per fire in a loop. - Spread: each instance is scattered by a random value within
±ambient_particles_dx/dy/dz, so a single point becomes a box-shaped cloud. Every instance in one fire shares the same picked particle id.
Shared Features
All three share the same tick-and-emit skeleton, which is what keeps them cheap on a populated server:
- Player activation radius: the tick does real work only while a player is within the configured radius. An unvisited NPC costs one data read and one compare per tick, so a map full of ambient emitters stays light until someone walks up.
- Restart-safe cadence: the next-fire time is persisted on the entity (
q.entity.data.ambient_sound_next_tickand the equivalents) as a monotonicgame_timevalue, so the schedule survives a server restart. - Pause condition: each exposes an optional Molang gate (
*_paused_condition) that skips a cycle when it assignsv.result = true. Using this gate you could make an effect pause depending on the time of day, weather, or even the state of other NPCs. - Back-off when idle: when nobody is in range, or the pause gate trips, the next check is scheduled about a second out instead of every tick.
The Ambient preset
The Ambient preset is an invisible NPC designed as a drop-in solution to uplift a scene with these behaviours. It uses the cobblemon:puzzle resource with the invis variation, hides its nametag, disables gravity, and ships with both ambient_sound_loop and ambient_particle_emitter already applied (alongside core, set_entity_properties, a small quarter_cube_hitbox, and a no-op interaction). It does not despawn.
The point is a marker a server owner can drop anywhere, enable the sound and/or particle channels on, and configure in place. Because the NPC is invisible and stationary, it layers onto an existing build without adding a model.
The Idle Animation Cycler is left off the preset, since an invisible NPC has no animations to play. It is meant to be added to a visible NPC directly, such as a Pokemon or Simple Dialogue NPC.
Techy Stuff
- Built on Cobblemon's behaviour and Molang systems, declared in JSON and dispatched through
run_scripttasks on theminecraft:idleactivity. - Each behaviour pairs a
*_tickscript with anundoscript, so toggling a variable re-applies cleanly on save and removing the behaviour tears down its state. - Random slot selection uses
math.random_integer. The cycler's weighting is a sum-and-roll over the per-slot weights, with a float-edge fallback guarding the exact upper boundary. - Sound and particle ids and animation names run through the shared
replace_placeholders_varhelper, so{{npc}}resolves the same way it does in the animation-interaction and dialogue behaviours. - The Ambient preset is a single NPC JSON (
apply_behaviours) that composes the existing emitter behaviours, so it inherits any fixes or variables they gain later.
Built for the Cobblewilds Cobblemon server, but available for commission.





