Skip to content

Persistent Particles

Persistent particles are automatic, condition-based particle effects that apply to Pokemon when specific criteria are met.

Unlike manually applied particles, persistent particles:

  • Activate automatically when conditions match
  • No player interaction required
  • Server-wide effects that apply to all Pokemon
  • Priority-based when multiple configs match
  • Performance optimized with smart rendering
  1. Each Tick: Every Pokemon entity is evaluated
  2. Condition Matching: Pokemon is checked against all persistent particle configs
  3. Priority Selection: Highest priority matching config is selected
  4. Rendering: Particles spawn at configured intervals
  5. Cleanup: Effects are removed when Pokemon despawns or conditions change

Persistent particles are configured in config/glamour/persistent_particles/*.json:

{
"id": "unique_identifier",
"particleResourceId": "cobblemon_particle_id",
"priority": 100,
"settings": {
"intervalTicks": 40,
"durationTicks": 10,
"particleCount": 3,
"radius": 1.0,
"offsetX": 0.0,
"offsetY": 2.0,
"offsetZ": 0.0,
"followEntity": true,
"maxDistance": 32,
"onlyWhenVisible": false,
"requiresLOS": false
},
"conditions": {
// Condition fields...
}
}

Match specific Pokemon species:

"conditions": {
"species": ["pikachu", "raichu", "cobblemon:mew"]
}
  • Accepts plain names (pikachu) or full resource IDs (cobblemon:mew)
  • Multiple species can be specified
  • Case-insensitive

Match Pokemon with specific forms or aspects:

"conditions": {
"forms": ["alolan", "galarian"],
"aspects": ["red_stripe"],
"anyAspects": ["blue", "green"],
"excludeAspects": ["yellow"]
}
  • forms: Pokemon must have one of these forms
  • aspects: Pokemon must have ALL listed aspects
  • anyAspects: Pokemon must have AT LEAST ONE listed aspect
  • excludeAspects: Pokemon must NOT have any listed aspects

Match Pokemon by type:

"conditions": {
"types": ["fire", "dragon"]
}

Pokemon must have at least one of the specified types.

Match only shiny or non-shiny Pokemon:

"conditions": {
"isShiny": true
}

Match Pokemon within level ranges:

"conditions": {
"level": {
"exact": 100
}
}

Or:

"conditions": {
"level": {
"min": 50,
"max": 100
}
}

Activate only in specific biomes:

"conditions": {
"biomes": ["plains", "forest", "desert"]
}

Uses Minecraft biome IDs.

Activate only in certain dimensions:

"conditions": {
"dimensions": ["minecraft:overworld", "minecraft:the_nether"]
}

Activate during specific times:

"conditions": {
"timeOfDay": ["day", "night", "sunrise", "sunset"]
}

Options: day, night, sunrise, sunset

Activate during specific weather:

"conditions": {
"weather": ["clear", "rain", "thunder"]
}

Options: clear, rain, thunder

Match wild vs owned Pokemon:

"conditions": {
"isWild": true
}

Or:

"conditions": {
"isOwned": true
}

Match Pokemon with specific health levels:

"conditions": {
"healthPercent": {
"min": 0,
"max": 25
}
}

Shows particles when health is between 0-25%.

Match Pokemon in or out of battle:

"conditions": {
"isBattling": true
}

Match stationary (shoulder-mounted or in specific states):

"conditions": {
"isStationary": true
}

When multiple configurations match a Pokemon, the highest priority wins.

// Generic shiny effect - low priority
{
"id": "shiny_sparkle",
"priority": 100,
"conditions": { "isShiny": true }
}
// Legendary Pokemon - medium priority
{
"id": "legendary_aura",
"priority": 150,
"conditions": {
"species": ["mewtwo", "lugia", "rayquaza"]
}
}
// Shiny legendary - high priority
{
"id": "shiny_legendary_epic",
"priority": 200,
"conditions": {
"species": ["mewtwo", "lugia", "rayquaza"],
"isShiny": true
}
}

A shiny Mewtwo would use shiny_legendary_epic (priority 200).

SettingDescriptionDefault
intervalTicksTicks between particle spawns100
durationTicksHow long each particle lasts20
SettingDescriptionDefault
particleCountParticles spawned per interval1
radiusSpawn radius around center point1.0
offsetXX-axis offset from Pokemon center0.0
offsetYY-axis offset from Pokemon center0.0
offsetZZ-axis offset from Pokemon center0.0
SettingDescriptionDefault
followEntityAttach particles to entity (true) or world position (false)true

followEntity: true - Particles move with the Pokemon followEntity: false - Particles spawn at world coordinates where Pokemon was

SettingDescriptionDefault
maxDistanceMax render distance in blocks32
onlyWhenVisibleUse view-cone heuristicfalse
requiresLOSRequire line-of-sight (expensive)false

maxDistance determines how far players can be from the Pokemon to see particles:

"settings": {
"maxDistance": 24
}

Lower values = better performance.

onlyWhenVisible - Cheap view-cone check:

"settings": {
"onlyWhenVisible": true
}

Particles only render if Pokemon is roughly in player’s field of view.

requiresLOS - Expensive raytrace check:

"settings": {
"requiresLOS": true
}

Particles only render if player has direct line-of-sight (no blocks between). Uses global LOS budget and caching to maintain performance.

Higher intervalTicks = better performance:

  • 20 ticks (1 second): Very frequent, higher load
  • 40 ticks (2 seconds): Balanced
  • 100 ticks (5 seconds): Infrequent, low load
{
"id": "shiny_sparkle",
"particleResourceId": "sparkle",
"priority": 100,
"settings": {
"intervalTicks": 40,
"durationTicks": 10,
"particleCount": 3,
"radius": 1.0,
"followEntity": true,
"maxDistance": 32
},
"conditions": {
"isShiny": true
}
}
{
"id": "legendary_aura",
"particleResourceId": "aura",
"priority": 150,
"settings": {
"intervalTicks": 20,
"durationTicks": 20,
"particleCount": 5,
"radius": 2.0,
"offsetY": 1.0,
"followEntity": true,
"maxDistance": 48,
"onlyWhenVisible": true
},
"conditions": {
"species": ["mewtwo", "lugia", "rayquaza", "dialga", "palkia"]
}
}
{
"id": "ghost_night_aura",
"particleResourceId": "purple_flame",
"priority": 80,
"settings": {
"intervalTicks": 30,
"durationTicks": 15,
"particleCount": 2,
"radius": 1.5,
"followEntity": true,
"maxDistance": 32
},
"conditions": {
"types": ["ghost"],
"timeOfDay": ["night"]
}
}
{
"id": "low_health_warning",
"particleResourceId": "red_sparkle",
"priority": 50,
"settings": {
"intervalTicks": 10,
"durationTicks": 5,
"particleCount": 1,
"radius": 0.5,
"followEntity": true,
"maxDistance": 16
},
"conditions": {
"healthPercent": {
"max": 25
}
}
}
/persistentparticles reload

Reloads all persistent particle configs without restarting the server.

/persistentparticles stats

Shows:

  • Active persistent particles
  • Render counts per tick
  • Performance metrics
  • Global limits
/persistentparticles debug <config_id>

Displays detailed information about a specific configuration including all settings and conditions.

  1. Start with High Priority: Reserve high priorities (200+) for very specific conditions
  2. Test Performance: Monitor with /persistentparticles stats
  3. Use Visibility Checks: Enable onlyWhenVisible for expensive effects
  4. Reasonable Intervals: Start with 40-100 tick intervals
  5. Limit Particle Count: More particles = more client rendering load
  6. Distance Culling: Use appropriate maxDistance for effect type
  1. Check conditions match with /persistentparticles debug <id>
  2. Verify particleResourceId exists in Cobblemon’s particle pack
  3. Ensure player is within maxDistance
  4. Check if a higher priority config is overriding
  1. Review stats with /persistentparticles stats
  2. Increase intervalTicks globally
  3. Reduce particleCount in configs
  4. Enable onlyWhenVisible for expensive effects
  5. Lower maxDistance on high-frequency effects